6614aa9070
refs https://github.com/TryGhost/Team/issues/2078 The check for creating free Tiers needs to be explicit, rather than implicit because the type is optional, we only want to error if it is explicitly passed as "free", not if it is missing as "paid".
144 lines
3.8 KiB
JavaScript
144 lines
3.8 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',
|
|
'currency',
|
|
'monthlyPrice',
|
|
'yearlyPrice',
|
|
'welcomePageURL'
|
|
];
|
|
|
|
for (const editableProperty of editableProperties) {
|
|
if (Reflect.has(data, editableProperty)) {
|
|
tier[editableProperty] = data[editableProperty];
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
};
|