0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Added offer read endpoint to content API (#14794)

refs TryGhost/Team#1599

- allows offer details to be fetched via content API directly
This commit is contained in:
Rishabh Garg 2022-05-12 18:16:10 +05:30 committed by GitHub
parent 3214186f98
commit 725c2673ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 1 deletions

View file

@ -219,5 +219,9 @@ module.exports = {
get newslettersPublic() {
return shared.pipeline(require('./newsletters-public'), localUtils, 'content');
},
get offersPublic() {
return shared.pipeline(require('./offers-public'), localUtils, 'content');
}
};

View file

@ -0,0 +1,28 @@
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const offersService = require('../../services/offers');
const messages = {
offerNotFound: 'Offer not found.'
};
module.exports = {
docName: 'offers',
read: {
data: ['id'],
permissions: true,
async query(frame) {
const offer = await offersService.api.getOffer(frame.data);
if (!offer) {
throw new errors.NotFoundError({
message: tpl(messages.offerNotFound)
});
}
frame.response = {
offers: [offer]
};
}
}
};

View file

@ -1,8 +1,16 @@
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:offers');
const utils = require('../../index');
module.exports = {
all() {
all(_models, _apiConfig, frame) {
debug('all');
// Offers has frame.response already set
// Cleanup response for content API
// TODO: remove and set explicit allowlist when moved to mapper
if (utils.isContentAPI(frame) && frame.response?.offers?.[0]) {
delete frame.response.offers[0].redemption_count;
delete frame.response.offers[0].code;
}
}
};

View file

@ -36,5 +36,7 @@ module.exports = function apiRoutes() {
router.get('/tiers', mw.authenticatePublic, http(api.tiersPublic.browse));
router.get('/newsletters', mw.authenticatePublic, http(api.newslettersPublic.browse));
router.get('/offers/:id', mw.authenticatePublic, http(api.offersPublic.read));
return router;
};

View file

@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Offers Content API Can read offer details from id 1: [body] 1`] = `
Object {
"offers": Array [
Object {
"amount": 12,
"cadence": "year",
"currency": null,
"currency_restriction": false,
"display_description": "",
"display_title": "",
"duration": "once",
"duration_in_months": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "Black Friday",
"status": "active",
"tier": Object {
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "Default Product",
},
"type": "percent",
},
],
}
`;
exports[`Offers Content API Can read offer details from id 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "331",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
"x-powered-by": "Express",
}
`;

View file

@ -0,0 +1,38 @@
const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework');
const testUtils = require('../../utils');
const models = require('../../../core/server/models');
const offerSnapshot = {
id: matchers.anyObjectId,
tier: {
id: matchers.anyObjectId
}
};
describe('Offers Content API', function () {
let agent;
before(async function () {
agent = await agentProvider.getContentAPIAgent();
await fixtureManager.init('api_keys', 'members');
agent.authenticate();
});
it('Can read offer details from id', async function () {
const productModel = await models.Product.findOne({type: 'paid'}, testUtils.context.internal);
const offerData = testUtils.DataGenerator.forKnex.createOffer({
product_id: productModel.get('id')
});
const offerModel = await models.Offer.add(offerData, {context: {internal: true}});
await agent.get(`/offers/${offerModel.get('id')}`)
.expectStatus(200)
.matchHeaderSnapshot({
etag: matchers.anyEtag
})
.matchBodySnapshot({
offers: Array(1).fill(offerSnapshot)
});
});
});

View file

@ -994,6 +994,25 @@ DataGenerator.forKnex = (function () {
});
}
function createOffer(overrides) {
const newObj = _.cloneDeep(overrides);
return _.defaults(newObj, {
id: ObjectId().toHexString(),
name: 'Black Friday',
code: 'black-friday',
display_title: 'Black Friday Sale!',
display_description: '10% off on yearly plan',
discount_type: 'percent',
interval: 'year',
discount_amount: 12,
duration: 'once',
duration_in_months: null,
currency_restriction: false,
currency: null,
active: true
});
}
function createMember(overrides) {
const newObj = _.cloneDeep(overrides);
@ -1554,6 +1573,7 @@ DataGenerator.forKnex = (function () {
createCustomThemeSetting: createBasic,
createProduct,
createNewsletter,
createOffer,
invites,
posts,