From 3e9a23355f77cac04634e08df1d5a996e7f68604 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Fri, 2 Jul 2021 22:21:44 +0530 Subject: [PATCH] Handled visibility filter in post/page API refs https://github.com/TryGhost/Team/issues/849 As part of work for segmented post access with multiple products, the custom filter for post access is stored in `visibility` field on posts but passed with `visibility_filter` property on API. This change - - updates input serializer of posts to transform `visibility` and `visibility_filter` properties correctly - updates output serializer for canary to transform and send `visibility_filter` attribute with filter value - updates output serializer for v3 to ignore any custom filter on visibility and return `paid` instead as v3 didn't have a concept of custom filter --- core/server/api/canary/utils/serializers/input/posts.js | 8 ++++++++ .../api/canary/utils/serializers/output/utils/clean.js | 9 +++++++++ core/server/api/v2/utils/validators/input/pages.js | 7 +++---- core/server/api/v2/utils/validators/input/posts.js | 7 +++---- .../api/v3/utils/serializers/output/utils/clean.js | 5 +++++ core/server/api/v3/utils/validators/input/pages.js | 7 +++---- core/server/api/v3/utils/validators/input/posts.js | 7 +++---- .../unit/api/canary/utils/validators/input/pages_spec.js | 3 ++- .../unit/api/canary/utils/validators/input/posts_spec.js | 3 ++- 9 files changed, 38 insertions(+), 18 deletions(-) diff --git a/core/server/api/canary/utils/serializers/input/posts.js b/core/server/api/canary/utils/serializers/input/posts.js index 3a392acde9..d7031f7b6c 100644 --- a/core/server/api/canary/utils/serializers/input/posts.js +++ b/core/server/api/canary/utils/serializers/input/posts.js @@ -111,6 +111,13 @@ 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'); @@ -205,6 +212,7 @@ module.exports = { }); } + transformPostVisibilityFilters(frame); transformLegacyEmailRecipientFilters(frame); handlePostsMeta(frame); defaultFormat(frame); diff --git a/core/server/api/canary/utils/serializers/output/utils/clean.js b/core/server/api/canary/utils/serializers/output/utils/clean.js index 3481532665..75c23e559c 100644 --- a/core/server/api/canary/utils/serializers/output/utils/clean.js +++ b/core/server/api/canary/utils/serializers/output/utils/clean.js @@ -1,5 +1,6 @@ const _ = require('lodash'); const localUtils = require('../../../index'); +const labsService = require('../../../../../../services/labs'); const tag = (attrs, frame) => { if (localUtils.isContentAPI(frame)) { @@ -120,6 +121,14 @@ 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; diff --git a/core/server/api/v2/utils/validators/input/pages.js b/core/server/api/v2/utils/validators/input/pages.js index ca532276b3..e61d609800 100644 --- a/core/server/api/v2/utils/validators/input/pages.js +++ b/core/server/api/v2/utils/validators/input/pages.js @@ -4,7 +4,7 @@ const {ValidationError} = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const messages = { - invalidVisibilityFilter: 'Invalid filter in visibility_filter property' + invalidVisibilityFilter: 'Invalid filter in visibility property' }; const validateVisibility = async function (frame) { @@ -14,17 +14,16 @@ const validateVisibility = async function (frame) { // validate visibility - not done at schema level because this can be an NQL query so needs model access const visibility = frame.data.pages[0].visibility; - const visibilityFilter = frame.data.pages[0].visibility_filter; if (visibility) { if (!['public', 'members', 'paid'].includes(visibility)) { // check filter is valid try { - await models.Member.findPage({filter: visibilityFilter, limit: 1}); + await models.Member.findPage({filter: visibility, limit: 1}); return Promise.resolve(); } catch (err) { return Promise.reject(new ValidationError({ message: tpl(messages.invalidVisibilityFilter), - property: 'visibility_filter' + property: 'visibility' })); } } diff --git a/core/server/api/v2/utils/validators/input/posts.js b/core/server/api/v2/utils/validators/input/posts.js index a0d8406e0e..b1b4243dfd 100644 --- a/core/server/api/v2/utils/validators/input/posts.js +++ b/core/server/api/v2/utils/validators/input/posts.js @@ -4,7 +4,7 @@ const {ValidationError} = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const messages = { - invalidVisibilityFilter: 'Invalid filter in visibility_filter property' + invalidVisibilityFilter: 'Invalid filter in visibility property' }; const validateVisibility = async function (frame) { @@ -14,17 +14,16 @@ const validateVisibility = async function (frame) { // validate visibility - not done at schema level because this can be an NQL query so needs model access const visibility = frame.data.posts[0].visibility; - const visibilityFilter = frame.data.posts[0].visibility_filter; if (visibility) { if (!['public', 'members', 'paid'].includes(visibility)) { // check filter is valid try { - await models.Member.findPage({filter: visibilityFilter, limit: 1}); + await models.Member.findPage({filter: visibility, limit: 1}); return Promise.resolve(); } catch (err) { return Promise.reject(new ValidationError({ message: tpl(messages.invalidVisibilityFilter), - property: 'visibility_filter' + property: 'visibility' })); } } diff --git a/core/server/api/v3/utils/serializers/output/utils/clean.js b/core/server/api/v3/utils/serializers/output/utils/clean.js index 31225f8dda..805036a74a 100644 --- a/core/server/api/v3/utils/serializers/output/utils/clean.js +++ b/core/server/api/v3/utils/serializers/output/utils/clean.js @@ -96,6 +96,11 @@ const post = (attrs, frame) => { if (attrs.og_description === '') { attrs.og_description = null; } + // Note: If visibility is set to a specific filter in v4, we want to return paid in v3 + if (attrs.visibility && !['public', 'members', 'paid'].includes(attrs.visibility)) { + attrs.visibility = 'paid'; + } + // NOTE: the visibility column has to be always present in Content API response to perform content gating if (columns && columns.includes('visibility') && fields && !fields.includes('visibility')) { delete attrs.visibility; diff --git a/core/server/api/v3/utils/validators/input/pages.js b/core/server/api/v3/utils/validators/input/pages.js index ca532276b3..e61d609800 100644 --- a/core/server/api/v3/utils/validators/input/pages.js +++ b/core/server/api/v3/utils/validators/input/pages.js @@ -4,7 +4,7 @@ const {ValidationError} = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const messages = { - invalidVisibilityFilter: 'Invalid filter in visibility_filter property' + invalidVisibilityFilter: 'Invalid filter in visibility property' }; const validateVisibility = async function (frame) { @@ -14,17 +14,16 @@ const validateVisibility = async function (frame) { // validate visibility - not done at schema level because this can be an NQL query so needs model access const visibility = frame.data.pages[0].visibility; - const visibilityFilter = frame.data.pages[0].visibility_filter; if (visibility) { if (!['public', 'members', 'paid'].includes(visibility)) { // check filter is valid try { - await models.Member.findPage({filter: visibilityFilter, limit: 1}); + await models.Member.findPage({filter: visibility, limit: 1}); return Promise.resolve(); } catch (err) { return Promise.reject(new ValidationError({ message: tpl(messages.invalidVisibilityFilter), - property: 'visibility_filter' + property: 'visibility' })); } } diff --git a/core/server/api/v3/utils/validators/input/posts.js b/core/server/api/v3/utils/validators/input/posts.js index a0d8406e0e..b1b4243dfd 100644 --- a/core/server/api/v3/utils/validators/input/posts.js +++ b/core/server/api/v3/utils/validators/input/posts.js @@ -4,7 +4,7 @@ const {ValidationError} = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const messages = { - invalidVisibilityFilter: 'Invalid filter in visibility_filter property' + invalidVisibilityFilter: 'Invalid filter in visibility property' }; const validateVisibility = async function (frame) { @@ -14,17 +14,16 @@ const validateVisibility = async function (frame) { // validate visibility - not done at schema level because this can be an NQL query so needs model access const visibility = frame.data.posts[0].visibility; - const visibilityFilter = frame.data.posts[0].visibility_filter; if (visibility) { if (!['public', 'members', 'paid'].includes(visibility)) { // check filter is valid try { - await models.Member.findPage({filter: visibilityFilter, limit: 1}); + await models.Member.findPage({filter: visibility, limit: 1}); return Promise.resolve(); } catch (err) { return Promise.reject(new ValidationError({ message: tpl(messages.invalidVisibilityFilter), - property: 'visibility_filter' + property: 'visibility' })); } } diff --git a/test/unit/api/canary/utils/validators/input/pages_spec.js b/test/unit/api/canary/utils/validators/input/pages_spec.js index 5aca494729..c82f090423 100644 --- a/test/unit/api/canary/utils/validators/input/pages_spec.js +++ b/test/unit/api/canary/utils/validators/input/pages_spec.js @@ -198,7 +198,8 @@ describe('Unit: canary/utils/validators/input/pages', function () { pages: [{ title: 'pass', authors: [{id: 'correct'}], - visibility: 'label:vip' + visibility: 'filter', + visibility_filter: 'label:vip' }] } }; diff --git a/test/unit/api/canary/utils/validators/input/posts_spec.js b/test/unit/api/canary/utils/validators/input/posts_spec.js index b33f8b975c..0fcbfc0e9a 100644 --- a/test/unit/api/canary/utils/validators/input/posts_spec.js +++ b/test/unit/api/canary/utils/validators/input/posts_spec.js @@ -198,7 +198,8 @@ describe('Unit: canary/utils/validators/input/posts', function () { posts: [{ title: 'pass', authors: [{id: 'correct'}], - visibility: 'label:vip' + visibility: 'filter', + visibility_filter: 'label:vip' }] } };