From aef8421235d8fff68d7d4f8b73f77484073311a5 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Thu, 12 Jan 2023 09:32:03 +0100 Subject: [PATCH] feat(medusa): Emit events on product category mutations (#3003) * chore: added events on product category mutation * chore: remove duplicates + refactor test --- .changeset/selfish-bulldogs-knock.md | 5 ++ .../services/__tests__/product-category.ts | 82 ++++++++++++++++--- .../medusa/src/services/product-category.ts | 51 ++++++++++-- 3 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 .changeset/selfish-bulldogs-knock.md diff --git a/.changeset/selfish-bulldogs-knock.md b/.changeset/selfish-bulldogs-knock.md new file mode 100644 index 0000000000..ef4483d352 --- /dev/null +++ b/.changeset/selfish-bulldogs-knock.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +feat(medusa): emit events on product category mutation diff --git a/packages/medusa/src/services/__tests__/product-category.ts b/packages/medusa/src/services/__tests__/product-category.ts index b08448f119..d298927697 100644 --- a/packages/medusa/src/services/__tests__/product-category.ts +++ b/packages/medusa/src/services/__tests__/product-category.ts @@ -1,5 +1,13 @@ import { IdMap, MockRepository, MockManager } from "medusa-test-utils" import ProductCategoryService from "../product-category" +import { EventBusService } from "../" + +const eventBusService = { + emit: jest.fn(), + withTransaction: function () { + return this + }, +} as unknown as EventBusService describe("ProductCategoryService", () => { const validProdCategoryId = "skinny-jeans" @@ -22,6 +30,7 @@ describe("ProductCategoryService", () => { const productCategoryService = new ProductCategoryService({ manager: MockManager, productCategoryRepository, + eventBusService, }) beforeEach(async () => { jest.clearAllMocks() }) @@ -65,6 +74,7 @@ describe("ProductCategoryService", () => { const productCategoryService = new ProductCategoryService({ manager: MockManager, productCategoryRepository, + eventBusService, }) beforeEach(async () => { jest.clearAllMocks() }) @@ -99,12 +109,15 @@ describe("ProductCategoryService", () => { describe("create", () => { const productCategoryRepository = MockRepository({ - findOne: query => Promise.resolve({ id: IdMap.getId("jeans") }), + findOne: (query) => Promise.resolve({ id: IdMap.getId(validProdCategoryId) }), + create: () => Promise.resolve({ id: IdMap.getId(validProdCategoryId) }), + save: (record) => Promise.resolve(record), }) const productCategoryService = new ProductCategoryService({ manager: MockManager, productCategoryRepository, + eventBusService, }) beforeEach(async () => { @@ -112,13 +125,24 @@ describe("ProductCategoryService", () => { }) it("successfully creates a product category", async () => { - await productCategoryService.create({ name: "jeans" }) + await productCategoryService.create({ name: validProdCategoryId }) expect(productCategoryRepository.create).toHaveBeenCalledTimes(1) expect(productCategoryRepository.create).toHaveBeenCalledWith({ - name: "jeans", + name: validProdCategoryId, }) }) + + it("emits a message on successful create", async () => { + await productCategoryService.create({ name: validProdCategoryId }) + + expect(eventBusService.emit).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledWith( + "product-category.created", { + "id": IdMap.getId(validProdCategoryId) + } + ) + }) }) describe("delete", () => { @@ -138,7 +162,7 @@ describe("ProductCategoryService", () => { } return Promise.resolve({ - id: IdMap.getId("jeans"), + id: IdMap.getId(validProdCategoryId), category_children: [] }) }, @@ -150,17 +174,18 @@ describe("ProductCategoryService", () => { const productCategoryService = new ProductCategoryService({ manager: MockManager, productCategoryRepository, + eventBusService, }) beforeEach(async () => { jest.clearAllMocks() }) it("successfully deletes a product category", async () => { const result = await productCategoryService.delete( - IdMap.getId("jeans") + IdMap.getId(validProdCategoryId) ) expect(productCategoryRepository.delete).toBeCalledTimes(1) - expect(productCategoryRepository.delete).toBeCalledWith(IdMap.getId("jeans")) + expect(productCategoryRepository.delete).toBeCalledWith(IdMap.getId(validProdCategoryId)) }) it("returns without failure on not-found product category id", async () => { @@ -179,6 +204,19 @@ describe("ProductCategoryService", () => { `Deleting ProductCategory (with-children) with category children is not allowed` ) }) + + it("emits a message on successful delete", async () => { + const result = await productCategoryService.delete( + IdMap.getId(validProdCategoryId) + ) + + expect(eventBusService.emit).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledWith( + "product-category.deleted", { + "id": IdMap.getId(validProdCategoryId) + } + ) + }) }) describe("update", () => { @@ -198,6 +236,7 @@ describe("ProductCategoryService", () => { const productCategoryService = new ProductCategoryService({ manager: MockManager, productCategoryRepository, + eventBusService, }) beforeEach(async () => { @@ -205,9 +244,11 @@ describe("ProductCategoryService", () => { }) it("successfully updates a product category", async () => { - await productCategoryService.update(IdMap.getId(validProdCategoryId), { - name: "bathrobes", - }) + await productCategoryService.update( + IdMap.getId(validProdCategoryId), { + name: "bathrobes", + } + ) expect(productCategoryRepository.save).toHaveBeenCalledTimes(1) expect(productCategoryRepository.save).toHaveBeenCalledWith({ @@ -217,13 +258,30 @@ describe("ProductCategoryService", () => { }) it("fails on not-found Id product category", async () => { - const error = await productCategoryService.update(IdMap.getId(invalidProdCategoryId), { - name: "bathrobes", - }).catch(e => e) + const error = await productCategoryService.update( + IdMap.getId(invalidProdCategoryId), { + name: "bathrobes", + } + ).catch(e => e) expect(error.message).toBe( `ProductCategory with id: ${IdMap.getId(invalidProdCategoryId)} was not found` ) }) + + it("emits a message on successful update", async () => { + const result = await productCategoryService.update( + IdMap.getId(validProdCategoryId), { + name: "bathrobes", + } + ) + + expect(eventBusService.emit).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledWith( + "product-category.updated", { + "id": IdMap.getId(validProdCategoryId) + } + ) + }) }) }) diff --git a/packages/medusa/src/services/product-category.ts b/packages/medusa/src/services/product-category.ts index 0d2865f08b..e9fbc29536 100644 --- a/packages/medusa/src/services/product-category.ts +++ b/packages/medusa/src/services/product-category.ts @@ -5,6 +5,7 @@ import { ProductCategory } from "../models" import { ProductCategoryRepository } from "../repositories/product-category" import { FindConfig, Selector, QuerySelector } from "../types/common" import { buildQuery } from "../utils" +import { EventBusService } from "." import { CreateProductCategoryInput, UpdateProductCategoryInput, @@ -12,6 +13,7 @@ import { type InjectedDependencies = { manager: EntityManager + eventBusService: EventBusService productCategoryRepository: typeof ProductCategoryRepository } @@ -19,15 +21,27 @@ type InjectedDependencies = { * Provides layer to manipulate product categories. */ class ProductCategoryService extends TransactionBaseService { - protected manager_: EntityManager protected readonly productCategoryRepo_: typeof ProductCategoryRepository + protected readonly eventBusService_: EventBusService protected transactionManager_: EntityManager | undefined + protected manager_: EntityManager - constructor({ manager, productCategoryRepository }: InjectedDependencies) { + static Events = { + CREATED: "product-category.created", + UPDATED: "product-category.updated", + DELETED: "product-category.deleted", + } + + constructor({ + manager, + productCategoryRepository, + eventBusService, + }: InjectedDependencies) { // eslint-disable-next-line prefer-rest-params super(arguments[0]) - this.manager_ = manager + this.manager_ = manager + this.eventBusService_ = eventBusService this.productCategoryRepo_ = productCategoryRepository } @@ -109,13 +123,20 @@ class ProductCategoryService extends TransactionBaseService { * @return created product category */ async create( - productCategory: CreateProductCategoryInput + productCategoryInput: CreateProductCategoryInput ): Promise { return await this.atomicPhase_(async (manager) => { const pcRepo = manager.getCustomRepository(this.productCategoryRepo_) - const productCategoryRecord = pcRepo.create(productCategory) + let productCategory = pcRepo.create(productCategoryInput) + productCategory = await pcRepo.save(productCategory) - return await pcRepo.save(productCategoryRecord) + await this.eventBusService_ + .withTransaction(manager) + .emit(ProductCategoryService.Events.CREATED, { + id: productCategory.id + }) + + return productCategory }) } @@ -134,7 +155,7 @@ class ProductCategoryService extends TransactionBaseService { this.productCategoryRepo_ ) - const productCategory = await this.retrieve(productCategoryId) + let productCategory = await this.retrieve(productCategoryId) for (const key in productCategoryInput) { if (isDefined(productCategoryInput[key])) { @@ -142,7 +163,15 @@ class ProductCategoryService extends TransactionBaseService { } } - return await productCategoryRepo.save(productCategory) + productCategory = await productCategoryRepo.save(productCategory) + + await this.eventBusService_ + .withTransaction(manager) + .emit(ProductCategoryService.Events.UPDATED, { + id: productCategory.id, + }) + + return productCategory }) } @@ -173,6 +202,12 @@ class ProductCategoryService extends TransactionBaseService { } await productCategoryRepository.delete(productCategory.id) + + await this.eventBusService_ + .withTransaction(manager) + .emit(ProductCategoryService.Events.DELETED, { + id: productCategory.id + }) }) } }