From 82ed10473b0b2afc100a461129b2178557f588ac Mon Sep 17 00:00:00 2001 From: Naz Date: Thu, 20 Oct 2022 14:56:57 +0800 Subject: [PATCH] Added 'getDefaultProduct' convenience method to product repo refs https://github.com/TryGhost/Team/issues/1869 - There are multiple places in the codebase fetching "default product". The code is slightly divergent in each one of them and has been a source of bugs (like the one referenced). Having the logic captured in one place will allow reducing the code duplication, making code less bug prone, and making testing the modules dependent on the "setDefaultProduct" method easier --- ghost/members-api/lib/repositories/product.js | 15 ++++++++++ .../unit/lib/repositories/product.test.js | 28 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 ghost/members-api/test/unit/lib/repositories/product.test.js diff --git a/ghost/members-api/lib/repositories/product.js b/ghost/members-api/lib/repositories/product.js index 9e03a77a61..51ee6c80c7 100644 --- a/ghost/members-api/lib/repositories/product.js +++ b/ghost/members-api/lib/repositories/product.js @@ -128,6 +128,21 @@ class ProductRepository { throw new NotFoundError({message: 'Missing id, slug, stripe_product_id or stripe_price_id from data'}); } + /** + * Fetches the default product + * @param {Object} options + * @returns {Promise} + */ + async getDefaultProduct(options = {}) { + const defaultProductPage = await this.list({ + filter: 'type:paid+active:true', + limit: 1, + ...options + }); + + return defaultProductPage.data[0]; + } + /** * Creates a product from a name * diff --git a/ghost/members-api/test/unit/lib/repositories/product.test.js b/ghost/members-api/test/unit/lib/repositories/product.test.js new file mode 100644 index 0000000000..b9ee5c3915 --- /dev/null +++ b/ghost/members-api/test/unit/lib/repositories/product.test.js @@ -0,0 +1,28 @@ +const assert = require('assert'); +const sinon = require('sinon'); +const ProductRepository = require('../../../../lib/repositories/product'); + +describe('MemberRepository', function () { + describe('getDefaultProduct', function () { + it('calls list method with specific parameters', async function () { + const productRepository = new ProductRepository({}); + const listStub = sinon.stub(productRepository, 'list').resolves({ + data: [{ + id: 'default_product_id' + }] + }); + + const defaultProduct = await productRepository.getDefaultProduct({ + withRelated: ['stripePrices'] + }); + + assert.ok(listStub.called); + + assert.equal(listStub.args[0][0].filter, 'type:paid+active:true', 'should only take into account paid and active records'); + assert.equal(listStub.args[0][0].limit, 1, 'should only fetch a single record'); + assert.deepEqual(listStub.args[0][0].withRelated, ['stripePrices'], 'should extend passed in options'); + + assert.equal(defaultProduct.id, 'default_product_id', 'returns a single product object'); + }); + }); +});