Updated serialization for handling tiers visibility
refs https://github.com/TryGhost/Team/issues/1071 Going forward, if the visibility of a page/post is set for specific tiers, we send a `tiers` array in API response that contains list of tiers with access. This change - - updates post/page mapper to transform existing data where `visibility` is a custom nql string to tiers array - updates default include for post/pages to include `products`, which allows attaching relevant tiers from the pivot table - cleans up usage of `visibility_filter` in serialization
This commit is contained in:
parent
354bb5c9a1
commit
7ab4c44475
@ -1,7 +1,7 @@
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const models = require('../../models');
|
||||
const ALLOWED_INCLUDES = ['authors', 'tags'];
|
||||
const ALLOWED_INCLUDES = ['authors', 'tags', 'tiers'];
|
||||
|
||||
const messages = {
|
||||
postNotFound: 'Post not found.'
|
||||
|
@ -1,7 +1,7 @@
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const models = require('../../models');
|
||||
const ALLOWED_INCLUDES = ['tags', 'authors'];
|
||||
const ALLOWED_INCLUDES = ['tags', 'authors', 'tiers'];
|
||||
|
||||
const messages = {
|
||||
pageNotFound: 'Page not found.'
|
||||
|
@ -2,7 +2,7 @@ const models = require('../../models');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const getPostServiceInstance = require('../../services/posts/posts-service');
|
||||
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles'];
|
||||
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers'];
|
||||
const UNSAFE_ATTRS = ['status', 'authors', 'visibility'];
|
||||
|
||||
const messages = {
|
||||
|
@ -1,7 +1,7 @@
|
||||
const models = require('../../models');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const allowedIncludes = ['tags', 'authors'];
|
||||
const allowedIncludes = ['tags', 'authors', 'tiers'];
|
||||
|
||||
const messages = {
|
||||
postNotFound: 'Post not found.'
|
||||
|
@ -2,7 +2,7 @@ const models = require('../../models');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const getPostServiceInstance = require('../../services/posts/posts-service');
|
||||
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
||||
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email', 'tiers'];
|
||||
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
||||
|
||||
const messages = {
|
||||
|
@ -38,7 +38,7 @@ function defaultRelations(frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles'];
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers'];
|
||||
}
|
||||
|
||||
function setDefaultOrder(frame) {
|
||||
@ -102,13 +102,6 @@ const forceStatusFilter = (frame) => {
|
||||
}
|
||||
};
|
||||
|
||||
const transformPageVisibilityFilters = (frame) => {
|
||||
if (frame.data.pages[0].visibility === 'filter' && frame.data.pages[0].visibility_filter) {
|
||||
frame.data.pages[0].visibility = frame.data.pages[0].visibility_filter;
|
||||
}
|
||||
delete frame.data.pages[0].visibility_filter;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
browse(apiConfig, frame) {
|
||||
debug('browse');
|
||||
@ -187,7 +180,6 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
transformPageVisibilityFilters(frame);
|
||||
handlePostsMeta(frame);
|
||||
defaultFormat(frame);
|
||||
defaultRelations(frame);
|
||||
|
@ -38,7 +38,7 @@ function defaultRelations(frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email'];
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers'];
|
||||
}
|
||||
|
||||
function setDefaultOrder(frame) {
|
||||
@ -111,13 +111,6 @@ const transformLegacyEmailRecipientFilters = (frame) => {
|
||||
}
|
||||
};
|
||||
|
||||
const transformPostVisibilityFilters = (frame) => {
|
||||
if (frame.data.posts[0].visibility === 'filter' && frame.data.posts[0].visibility_filter) {
|
||||
frame.data.posts[0].visibility = frame.data.posts[0].visibility_filter;
|
||||
}
|
||||
delete frame.data.posts[0].visibility_filter;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
browse(apiConfig, frame) {
|
||||
debug('browse');
|
||||
@ -212,7 +205,6 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
transformPostVisibilityFilters(frame);
|
||||
transformLegacyEmailRecipientFilters(frame);
|
||||
handlePostsMeta(frame);
|
||||
defaultFormat(frame);
|
||||
|
@ -2,8 +2,8 @@ const mapper = require('./utils/mapper');
|
||||
const gating = require('./utils/post-gating');
|
||||
|
||||
module.exports = {
|
||||
read(model, apiConfig, frame) {
|
||||
const emailPost = mapper.mapPost(model, frame);
|
||||
async read(model, apiConfig, frame) {
|
||||
const emailPost = await mapper.mapPost(model, frame);
|
||||
gating.forPost(emailPost, frame);
|
||||
|
||||
frame.response = {
|
||||
|
@ -2,25 +2,29 @@ const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:pa
|
||||
const mapper = require('./utils/mapper');
|
||||
|
||||
module.exports = {
|
||||
all(models, apiConfig, frame) {
|
||||
async all(models, apiConfig, frame) {
|
||||
debug('all');
|
||||
|
||||
// CASE: e.g. destroy returns null
|
||||
if (!models) {
|
||||
return;
|
||||
}
|
||||
|
||||
let pages = [];
|
||||
if (models.meta) {
|
||||
for (let model of models.data) {
|
||||
let page = await mapper.mapPage(model, frame);
|
||||
pages.push(page);
|
||||
}
|
||||
frame.response = {
|
||||
pages: models.data.map(model => mapper.mapPage(model, frame)),
|
||||
pages,
|
||||
meta: models.meta
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let page = await mapper.mapPage(models, frame);
|
||||
frame.response = {
|
||||
pages: [mapper.mapPage(models, frame)]
|
||||
pages: [page]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -2,25 +2,29 @@ const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:po
|
||||
const mapper = require('./utils/mapper');
|
||||
|
||||
module.exports = {
|
||||
all(models, apiConfig, frame) {
|
||||
async all(models, apiConfig, frame) {
|
||||
debug('all');
|
||||
|
||||
// CASE: e.g. destroy returns null
|
||||
if (!models) {
|
||||
return;
|
||||
}
|
||||
|
||||
let posts = [];
|
||||
if (models.meta) {
|
||||
for (let model of models.data) {
|
||||
let post = await mapper.mapPost(model, frame);
|
||||
posts.push(post);
|
||||
}
|
||||
frame.response = {
|
||||
posts: models.data.map(model => mapper.mapPost(model, frame)),
|
||||
posts,
|
||||
meta: models.meta
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let post = await mapper.mapPost(models, frame);
|
||||
frame.response = {
|
||||
posts: [mapper.mapPost(models, frame)]
|
||||
posts: [post]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
const mapper = require('./utils/mapper');
|
||||
|
||||
module.exports = {
|
||||
all(model, apiConfig, frame) {
|
||||
async all(model, apiConfig, frame) {
|
||||
const data = await mapper.mapPost(model, frame);
|
||||
frame.response = {
|
||||
preview: [mapper.mapPost(model, frame)]
|
||||
preview: [data]
|
||||
};
|
||||
frame.response.preview[0].page = model.get('type') === 'page';
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const localUtils = require('../../../index');
|
||||
const labsService = require('../../../../../../../shared/labs');
|
||||
|
||||
const tag = (attrs, frame) => {
|
||||
if (localUtils.isContentAPI(frame)) {
|
||||
@ -122,14 +121,6 @@ const post = (attrs, frame) => {
|
||||
delete attrs.primary_author;
|
||||
}
|
||||
|
||||
// Handles visibility filter for multiple products
|
||||
if (attrs.visibility && labsService.isSet('multipleProducts')) {
|
||||
if (!['members', 'public', 'paid'].includes(attrs.visibility)) {
|
||||
attrs.visibility_filter = attrs.visibility;
|
||||
attrs.visibility = 'filter';
|
||||
}
|
||||
}
|
||||
|
||||
delete attrs.locale;
|
||||
delete attrs.author;
|
||||
delete attrs.type;
|
||||
|
@ -7,6 +7,10 @@ const clean = require('./clean');
|
||||
const extraAttrs = require('./extra-attrs');
|
||||
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
||||
const mega = require('../../../../../../services/mega');
|
||||
const labsService = require('../../../../../../../shared/labs');
|
||||
|
||||
const getPostServiceInstance = require('../../../../../../services/posts/posts-service');
|
||||
const postsService = getPostServiceInstance('canary');
|
||||
|
||||
const mapUser = (model, frame) => {
|
||||
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
||||
@ -27,7 +31,7 @@ const mapTag = (model, frame) => {
|
||||
return jsonModel;
|
||||
};
|
||||
|
||||
const mapPost = (model, frame) => {
|
||||
const mapPost = async (model, frame) => {
|
||||
const extendedOptions = Object.assign(_.cloneDeep(frame.options), {
|
||||
extraProperties: ['canonical_url']
|
||||
});
|
||||
@ -38,6 +42,17 @@ const mapPost = (model, frame) => {
|
||||
|
||||
extraAttrs.forPost(frame, model, jsonModel);
|
||||
|
||||
// Attach tiers to custom nql visibility filter
|
||||
if (labsService.isSet('multipleProducts')
|
||||
&& jsonModel.visibility
|
||||
&& !['members', 'public', 'paid', 'tiers'].includes(jsonModel.visibility)
|
||||
) {
|
||||
const tiers = await postsService.getProductsFromVisibilityFilter(jsonModel.visibility);
|
||||
|
||||
jsonModel.visibility = 'tiers';
|
||||
jsonModel.tiers = tiers;
|
||||
}
|
||||
|
||||
if (utils.isContentAPI(frame)) {
|
||||
// Content api v2 still expects page prop
|
||||
if (jsonModel.type === 'page') {
|
||||
@ -88,8 +103,8 @@ const mapPost = (model, frame) => {
|
||||
return jsonModel;
|
||||
};
|
||||
|
||||
const mapPage = (model, frame) => {
|
||||
const jsonModel = mapPost(model, frame);
|
||||
const mapPage = async (model, frame) => {
|
||||
const jsonModel = await mapPost(model, frame);
|
||||
|
||||
delete jsonModel.email_subject;
|
||||
delete jsonModel.email_recipient_filter;
|
||||
|
@ -1,8 +1,10 @@
|
||||
const nql = require('@nexes/nql');
|
||||
const {BadRequestError} = require('@tryghost/errors');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
|
||||
const messages = {
|
||||
invalidEmailRecipientFilter: 'Invalid filter in email_recipient_filter param.'
|
||||
invalidEmailRecipientFilter: 'Invalid filter in email_recipient_filter param.',
|
||||
invalidVisibilityFilter: 'Invalid visibility filter.'
|
||||
};
|
||||
|
||||
class PostsService {
|
||||
@ -78,6 +80,28 @@ class PostsService {
|
||||
return model;
|
||||
}
|
||||
|
||||
async getProductsFromVisibilityFilter(visibilityFilter) {
|
||||
try {
|
||||
const allProducts = await this.models.Product.findAll();
|
||||
const visibilityFilterJson = nql(visibilityFilter).toJSON();
|
||||
const productsData = (visibilityFilterJson.product ? [visibilityFilterJson] : visibilityFilterJson.$or) || [];
|
||||
const tiers = productsData
|
||||
.map((data) => {
|
||||
return allProducts.find((p) => {
|
||||
return p.get('slug') === data.product;
|
||||
});
|
||||
}).filter(p => !!p).map((d) => {
|
||||
return d.toJSON();
|
||||
});
|
||||
return tiers;
|
||||
} catch (err) {
|
||||
return Promise.reject(new BadRequestError({
|
||||
message: tpl(messages.invalidVisibilityFilter),
|
||||
context: err.message
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates if the email should be tried to be sent out
|
||||
* @private
|
||||
|
Loading…
Reference in New Issue
Block a user