Ghost/ghost/tiers/lib/TiersAPI.js
Fabien "egg" O'Carroll 5f928794c3 Added support for Tier events
refs https://github.com/TryGhost/Team/issues/2078

These events are all required for other parts of the Ghost system to stay in
sync. The events on each Tier object will be dispatched by the TierRepository
once they've been persisted.

TierCreatedEvent - generate Stripe Products & Prices
TierNameChangeEvent - update Stripe Products
TierPriceChangeEvent - update Stripe Products & Prices
TierArchivedEvent - update the Portal settings for visible tiers
                  - disable Stripe Products & Prices
TierActivatedEvent - enable Stripe Products & Prices
2022-10-20 17:31:33 +07:00

147 lines
4.0 KiB
JavaScript

const ObjectID = require('bson-objectid').default;
const {BadRequestError} = require('@tryghost/errors');
const Tier = require('./Tier');
/**
* @typedef {object} ITierRepository
* @prop {(id: ObjectID) => Promise<Tier>} getById
* @prop {(tier: Tier) => Promise<void>} save
* @prop {(options?: {filter?: string}) => Promise<Tier[]>} getAll
*/
/**
* @typedef {object} ISlugService
* @prop {(input: string) => Promise<string>} generate
*/
/**
* @template {Model}
* @typedef {object} Page<Model>
* @prop {Model[]} data
* @prop {object} meta
* @prop {object} meta.pagination
* @prop {number} meta.pagination.page - The current page
* @prop {number} meta.pagination.pages - The total number of pages
* @prop {number} meta.pagination.limit - The limit of models per page
* @prop {number} meta.pagination.total - The totaL number of models across all pages
* @prop {number|null} meta.pagination.prev - The number of the previous page, or null if there isn't one
* @prop {number|null} meta.pagination.next - The number of the next page, or null if there isn't one
*/
module.exports = class TiersAPI {
/** @type {ITierRepository} */
#repository;
/** @type {ISlugService} */
#slugService;
constructor(deps) {
this.#repository = deps.repository;
this.#slugService = deps.slugService;
}
/**
* @param {object} [options]
* @param {string} [options.filter] - An NQL filter string
*
* @returns {Promise<Page<Tier>>}
*/
async browse(options = {}) {
const tiers = await this.#repository.getAll(options);
return {
data: tiers,
meta: {
pagination: {
page: 1,
pages: 1,
limit: tiers.length,
total: tiers.length,
prev: null,
next: null
}
}
};
}
/**
* @param {string} idString
*
* @returns {Promise<Tier>}
*/
async read(idString) {
const id = ObjectID.createFromHexString(idString);
const tier = await this.#repository.getById(id);
return tier;
}
/**
* @param {string} id
* @param {object} data
* @returns {Promise<Tier>}
*/
async edit(idString, data) {
const id = ObjectID.createFromHexString(idString);
const tier = await this.#repository.getById(id);
const editableProperties = [
'name',
'benefits',
'description',
'visibility',
'active',
'trialDays',
'welcomePageURL'
];
for (const editableProperty of editableProperties) {
if (Reflect.has(data, editableProperty)) {
tier[editableProperty] = data[editableProperty];
}
}
tier.updatePricing({
currency: data.currency || tier.currency,
monthlyPrice: data.monthlyPrice || tier.monthlyPrice,
yearlyPrice: data.yearlyPrice || tier.yearlyPrice
});
await this.#repository.save(tier);
return tier;
}
/**
* @param {object} data
* @returns {Promise<Tier>}
*/
async add(data) {
if (data.type === 'free') {
throw new BadRequestError({
message: 'Cannot create free Tier'
});
}
const slug = await this.#slugService.generate(data.slug || data.name);
const tier = await Tier.create({
slug,
type: 'paid',
status: 'active',
visibility: data.visibility,
name: data.name,
description: data.description,
benefits: data.benefits,
welcomePageURL: data.welcomePageURL,
monthlyPrice: data.monthlyPrice,
yearlyPrice: data.yearlyPrice,
currency: data.currency,
trialDays: data.trialDays
});
await this.#repository.save(tier);
return tier;
}
};