From 7e14da1225983dd58faabbd6d555818277ad4cc8 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Wed, 27 Jan 2021 11:13:24 +0100 Subject: [PATCH] feat: adds discount code search functionality (#155) --- .../medusa-test-utils/src/mock-repository.js | 7 +++ .../discounts/__tests__/list-discounts.js | 49 +++++++++++++----- .../routes/admin/discounts/list-discounts.js | 20 ++++++-- .../medusa/src/services/__mocks__/discount.js | 3 ++ .../medusa/src/services/__tests__/discount.js | 51 +++++++++++++++++++ packages/medusa/src/services/discount.js | 46 +++++++++++++++++ 6 files changed, 160 insertions(+), 16 deletions(-) diff --git a/packages/medusa-test-utils/src/mock-repository.js b/packages/medusa-test-utils/src/mock-repository.js index adde45066d..d81167b0fd 100644 --- a/packages/medusa-test-utils/src/mock-repository.js +++ b/packages/medusa-test-utils/src/mock-repository.js @@ -7,6 +7,7 @@ export default ({ findOne, findOneOrFail, save, + findAndCount, } = {}) => { return { create: jest.fn().mockImplementation((...args) => { @@ -63,5 +64,11 @@ export default ({ } return Promise.resolve(...args); }), + findAndCount: jest.fn().mockImplementation((...args) => { + if (findAndCount) { + return findAndCount(...args); + } + return {}; + }), }; }; diff --git a/packages/medusa/src/api/routes/admin/discounts/__tests__/list-discounts.js b/packages/medusa/src/api/routes/admin/discounts/__tests__/list-discounts.js index f98398511f..9c9198f38a 100644 --- a/packages/medusa/src/api/routes/admin/discounts/__tests__/list-discounts.js +++ b/packages/medusa/src/api/routes/admin/discounts/__tests__/list-discounts.js @@ -1,9 +1,10 @@ import { IdMap } from "medusa-test-utils" +import { defaultFields, defaultRelations } from ".." import { request } from "../../../../../helpers/test-request" import { DiscountServiceMock } from "../../../../../services/__mocks__/discount" describe("GET /admin/discounts", () => { - describe("successful retrieval", () => { + describe("successful retrieval of discounts", () => { let subject beforeAll(async () => { @@ -21,31 +22,55 @@ describe("GET /admin/discounts", () => { expect(subject.status).toEqual(200) }) - it("calls service retrieve", () => { - expect(DiscountServiceMock.list).toHaveBeenCalledTimes(1) + it("calls service retrieve with config", () => { + expect(DiscountServiceMock.listAndCount).toHaveBeenCalledTimes(1) + expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith( + {}, + { + select: defaultFields, + relations: defaultRelations, + skip: 0, + take: 20, + order: { created_at: "DESC" }, + } + ) }) }) - describe("expand region filter", () => { + describe("successful retrieval of discounts with query config", () => { let subject beforeAll(async () => { jest.clearAllMocks() - subject = await request("GET", `/admin/discounts?expand_fields=regions`, { - adminSession: { - jwt: { - userId: IdMap.getId("admin_user"), + subject = await request( + "GET", + `/admin/discounts?q=OLI&limit=40&offset=20&is_dynamic=false`, + { + adminSession: { + jwt: { + userId: IdMap.getId("admin_user"), + }, }, - }, - }) + } + ) }) it("returns 200", () => { expect(subject.status).toEqual(200) }) - it("calls service retrieve", () => { - expect(DiscountServiceMock.list).toHaveBeenCalledTimes(1) + it("calls service retrieve with config", () => { + expect(DiscountServiceMock.listAndCount).toHaveBeenCalledTimes(1) + expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith( + { q: "OLI", is_dynamic: false }, + { + select: defaultFields, + relations: defaultRelations, + skip: 20, + take: 40, + order: { created_at: "DESC" }, + } + ) }) }) }) diff --git a/packages/medusa/src/api/routes/admin/discounts/list-discounts.js b/packages/medusa/src/api/routes/admin/discounts/list-discounts.js index 4e9dee5d0a..eec1846a8d 100644 --- a/packages/medusa/src/api/routes/admin/discounts/list-discounts.js +++ b/packages/medusa/src/api/routes/admin/discounts/list-discounts.js @@ -2,23 +2,35 @@ import { defaultFields, defaultRelations } from "./" export default async (req, res) => { try { - const selector = {} + const discountService = req.scope.resolve("discountService") const limit = parseInt(req.query.limit) || 20 const offset = parseInt(req.query.offset) || 0 - const discountService = req.scope.resolve("discountService") + let selector = {} + + if ("q" in req.query) { + selector.q = req.query.q + } + + if ("is_dynamic" in req.query) { + selector.is_dynamic = req.query.is_dynamic === "true" + } const listConfig = { select: defaultFields, relations: defaultRelations, skip: offset, take: limit, + order: { created_at: "DESC" }, } - const discounts = await discountService.list(selector, listConfig) + const [discounts, count] = await discountService.listAndCount( + selector, + listConfig + ) - res.status(200).json({ discounts }) + res.status(200).json({ discounts, count, offset, limit }) } catch (err) { throw err } diff --git a/packages/medusa/src/services/__mocks__/discount.js b/packages/medusa/src/services/__mocks__/discount.js index c880b26a9e..d2f5ffe4b2 100644 --- a/packages/medusa/src/services/__mocks__/discount.js +++ b/packages/medusa/src/services/__mocks__/discount.js @@ -165,6 +165,9 @@ export const DiscountServiceMock = { list: jest.fn().mockImplementation(data => { return Promise.resolve([{}]) }), + listAndCount: jest.fn().mockImplementation(data => { + return Promise.resolve([{}]) + }), addRegion: jest.fn().mockReturnValue(Promise.resolve()), removeRegion: jest.fn().mockReturnValue(Promise.resolve()), addValidProduct: jest.fn().mockReturnValue(Promise.resolve()), diff --git a/packages/medusa/src/services/__tests__/discount.js b/packages/medusa/src/services/__tests__/discount.js index 0ec55a1f92..ff47e6e0d5 100644 --- a/packages/medusa/src/services/__tests__/discount.js +++ b/packages/medusa/src/services/__tests__/discount.js @@ -468,4 +468,55 @@ describe("DiscountService", () => { expect(discountRepository.save).toHaveBeenCalledTimes(0) }) }) + + describe("listAndCount", () => { + const discountRepository = MockRepository({ + findAndCount: () => + Promise.resolve([ + { + id: IdMap.getId("total10"), + code: "OLITEST", + }, + ]), + }) + + const discountService = new DiscountService({ + manager: MockManager, + discountRepository, + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("calls repository function with query and default config", async () => { + await discountService.listAndCount({ q: "OLI" }) + + expect(discountRepository.findAndCount).toHaveBeenCalledTimes(1) + expect(discountRepository.findAndCount).toHaveBeenCalledWith({ + join: { + alias: "discount", + }, + where: expect.anything(), + skip: 0, + take: 50, + order: { created_at: "DESC" }, + }) + }) + + it("calls repository function specified query", async () => { + await discountService.listAndCount( + {}, + { skip: 50, take: 50, order: { created_at: "ASC" } } + ) + + expect(discountRepository.findAndCount).toHaveBeenCalledTimes(1) + expect(discountRepository.findAndCount).toHaveBeenCalledWith({ + where: {}, + skip: 50, + take: 50, + order: { created_at: "ASC" }, + }) + }) + }) }) diff --git a/packages/medusa/src/services/discount.js b/packages/medusa/src/services/discount.js index f060c56df4..2b806f1c94 100644 --- a/packages/medusa/src/services/discount.js +++ b/packages/medusa/src/services/discount.js @@ -2,6 +2,7 @@ import _ from "lodash" import randomize from "randomatic" import { BaseService } from "medusa-interfaces" import { Validator, MedusaError } from "medusa-core-utils" +import { Brackets } from "typeorm" /** * Provides layer to manipulate discounts. @@ -115,6 +116,51 @@ class DiscountService extends BaseService { return discountRepo.find(query) } + /** + * @param {Object} selector - the query object for find + * @return {Promise} the result of the find operation + */ + async listAndCount( + selector = {}, + config = { skip: 0, take: 50, order: { created_at: "DESC" } } + ) { + const discountRepo = this.manager_.getCustomRepository( + this.discountRepository_ + ) + + let q + if ("q" in selector) { + q = selector.q + delete selector.q + } + + const query = this.buildQuery_(selector, config) + + if (q) { + const where = query.where + + delete where.code + + query.join = { + alias: "discount", + } + + query.where = qb => { + qb.where(where) + + qb.andWhere( + new Brackets(qb => { + qb.where(`discount.code ILIKE :q`, { q: `%${q}%` }) + }) + ) + } + } + + const [discounts, count] = await discountRepo.findAndCount(query) + + return [discounts, count] + } + /** * Creates a discount with provided data given that the data is validated. * Normalizes discount code to uppercase.