From 3fbb8aa671e6e12337a4f50b89696b913a4735c9 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Thu, 6 Jun 2024 13:47:38 +0200 Subject: [PATCH] feat: Product module events (#7598) WIP, not ready to review **what** - add events - integration tests of emitted events - remove integration tests on auto generated services in favor of module method integration tests --- packages/core/utils/src/product/events.ts | 15 + packages/core/utils/src/product/index.ts | 2 + .../index.ts => product-category.ts} | 21 +- .../product-categories.spec.ts | 12 +- .../product-collections.spec.ts | 10 +- .../product-options.spec.ts | 9 +- .../product-tags.spec.ts | 9 +- .../product-types.spec.ts | 9 +- .../product-variants.spec.ts | 112 +++++- .../product-module-service/products.spec.ts | 17 +- .../{services/product/index.ts => product.ts} | 23 +- .../services/product-collection/index.ts | 370 ------------------ .../services/product-option/index.ts | 369 ----------------- .../__tests__/services/product-tag/index.ts | 336 ---------------- .../__tests__/services/product-type/index.ts | 276 ------------- .../services/product-variant/index.ts | 321 --------------- .../src/services/product-module-service.ts | 32 +- packages/modules/product/src/types/index.ts | 2 + packages/modules/product/src/utils/events.ts | 102 +++++ packages/modules/product/src/utils/index.ts | 1 + 20 files changed, 292 insertions(+), 1756 deletions(-) create mode 100644 packages/core/utils/src/product/events.ts rename packages/modules/product/integration-tests/__tests__/{services/product-category/index.ts => product-category.ts} (98%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/product-categories.spec.ts (98%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/product-collections.spec.ts (98%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/product-options.spec.ts (97%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/product-tags.spec.ts (97%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/product-types.spec.ts (96%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/product-variants.spec.ts (72%) rename packages/modules/product/integration-tests/__tests__/{services => }/product-module-service/products.spec.ts (98%) rename packages/modules/product/integration-tests/__tests__/{services/product/index.ts => product.ts} (97%) delete mode 100644 packages/modules/product/integration-tests/__tests__/services/product-collection/index.ts delete mode 100644 packages/modules/product/integration-tests/__tests__/services/product-option/index.ts delete mode 100644 packages/modules/product/integration-tests/__tests__/services/product-tag/index.ts delete mode 100644 packages/modules/product/integration-tests/__tests__/services/product-type/index.ts delete mode 100644 packages/modules/product/integration-tests/__tests__/services/product-variant/index.ts create mode 100644 packages/modules/product/src/utils/events.ts create mode 100644 packages/modules/product/src/utils/index.ts diff --git a/packages/core/utils/src/product/events.ts b/packages/core/utils/src/product/events.ts new file mode 100644 index 0000000000..a095dc6d4b --- /dev/null +++ b/packages/core/utils/src/product/events.ts @@ -0,0 +1,15 @@ +import { buildEventNamesFromEntityName } from "../event-bus" +import { Modules } from "../modules-sdk" + +const eventBaseNames: [ + "product", + "productVariant", + "productOption", + "productType", + "productTag" +] = ["product", "productVariant", "productOption", "productType", "productTag"] + +export const ProductEvents = buildEventNamesFromEntityName( + eventBaseNames, + Modules.PRODUCT +) diff --git a/packages/core/utils/src/product/index.ts b/packages/core/utils/src/product/index.ts index a00fdcf0d0..26b7c25691 100644 --- a/packages/core/utils/src/product/index.ts +++ b/packages/core/utils/src/product/index.ts @@ -4,3 +4,5 @@ export enum ProductStatus { PUBLISHED = "published", REJECTED = "rejected", } + +export * from "./events" diff --git a/packages/modules/product/integration-tests/__tests__/services/product-category/index.ts b/packages/modules/product/integration-tests/__tests__/product-category.ts similarity index 98% rename from packages/modules/product/integration-tests/__tests__/services/product-category/index.ts rename to packages/modules/product/integration-tests/__tests__/product-category.ts index ab8bf465cb..3993955cf4 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-category/index.ts +++ b/packages/modules/product/integration-tests/__tests__/product-category.ts @@ -1,28 +1,29 @@ import { ProductCategoryService } from "@services" import { Modules } from "@medusajs/modules-sdk" -import { IProductModuleService } from "@medusajs/types" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -import { createProductCategories } from "../../../__fixtures__/product-category" +import { moduleIntegrationTestRunner } from "medusa-test-utils" +import { createProductCategories } from "../__fixtures__/product-category" import { eletronicsCategoriesData, productCategoriesData, productCategoriesRankData, -} from "../../../__fixtures__/product-category/data" +} from "../__fixtures__/product-category/data" +import { IProductModuleService } from "@medusajs/types" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +type Service = IProductModuleService & { + productCategoryService_: ProductCategoryService +} + +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service: moduleService }) => { describe("Product category Service", () => { let service: ProductCategoryService beforeEach(() => { - service = medusaApp.modules["productService"].productCategoryService_ + service = moduleService.productCategoryService_ }) describe("list", () => { diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/product-categories.spec.ts similarity index 98% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/product-categories.spec.ts index 29cf2c2eba..a6b9b46781 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/product-categories.spec.ts @@ -4,23 +4,19 @@ import { ProductStatus } from "@medusajs/utils" import { Product, ProductCategory } from "@models" import { MockEventBusService, - SuiteOptions, moduleIntegrationTestRunner, } from "medusa-test-utils" -import { createProductCategories } from "../../../__fixtures__/product-category" -import { productCategoriesRankData } from "../../../__fixtures__/product-category/data" +import { createProductCategories } from "../../__fixtures__/product-category" +import { productCategoriesRankData } from "../../__fixtures__/product-category/data" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, injectedDependencies: { eventBusModuleService: new MockEventBusService(), }, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service }) => { describe("ProductModuleService product categories", () => { let productOne: Product let productTwo: Product diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/product-collections.spec.ts similarity index 98% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/product-collections.spec.ts index d575350e5e..4b047534ce 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/product-collections.spec.ts @@ -4,22 +4,18 @@ import { ProductStatus } from "@medusajs/utils" import { Product, ProductCollection } from "@models" import { MockEventBusService, - SuiteOptions, moduleIntegrationTestRunner, } from "medusa-test-utils" -import { createCollections } from "../../../__fixtures__/product" +import { createCollections } from "../../__fixtures__/product" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, injectedDependencies: { eventBusModuleService: new MockEventBusService(), }, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service }) => { describe("ProductModuleService product collections", () => { let productOne: Product let productTwo: Product diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/product-options.spec.ts similarity index 97% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/product-options.spec.ts index dcc848e063..80ca3a64b8 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/product-options.spec.ts @@ -2,16 +2,13 @@ import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService } from "@medusajs/types" import { ProductStatus } from "@medusajs/utils" import { Product, ProductOption } from "@models" -import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils" +import { moduleIntegrationTestRunner } from "medusa-test-utils" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service }) => { describe("ProductModuleService product options", () => { let optionOne: ProductOption let optionTwo: ProductOption diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/product-tags.spec.ts similarity index 97% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/product-tags.spec.ts index e8c6583e83..f825a570b0 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/product-tags.spec.ts @@ -2,16 +2,13 @@ import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService } from "@medusajs/types" import { ProductStatus } from "@medusajs/utils" import { Product, ProductTag } from "@models" -import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils" +import { moduleIntegrationTestRunner } from "medusa-test-utils" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service }) => { describe("ProductModuleService product tags", () => { let tagOne: ProductTag let tagTwo: ProductTag diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/product-types.spec.ts similarity index 96% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/product-types.spec.ts index 3719550fbe..b4831a1f04 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/product-types.spec.ts @@ -1,16 +1,13 @@ import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService } from "@medusajs/types" import { ProductType } from "@models" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { moduleIntegrationTestRunner } from "medusa-test-utils" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service }) => { describe("ProductModuleService product types", () => { let typeOne: ProductType let typeTwo: ProductType diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/product-variants.spec.ts similarity index 72% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/product-variants.spec.ts index 2540a5f79c..36952c63e0 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/product-variants.spec.ts @@ -1,23 +1,43 @@ import { Modules } from "@medusajs/modules-sdk" -import { IProductModuleService } from "@medusajs/types" -import { ProductStatus } from "@medusajs/utils" -import { Product, ProductVariant } from "@models" +import { + CreateProductDTO, + CreateProductVariantDTO, + IProductModuleService, + ProductDTO, + ProductVariantDTO, +} from "@medusajs/types" +import { + CommonEvents, + composeMessage, + ProductEvents, + ProductStatus, +} from "@medusajs/utils" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { + MockEventBusService, + moduleIntegrationTestRunner, +} from "medusa-test-utils" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ service }) => { + let eventBusEmitSpy + + beforeEach(() => { + eventBusEmitSpy = jest.spyOn(MockEventBusService.prototype, "emit") + }) + + afterEach(() => { + jest.clearAllMocks() + }) + describe("ProductModuleService product variants", () => { - let variantOne: ProductVariant - let variantTwo: ProductVariant - let productOne: Product - let productTwo: Product + let variantOne: ProductVariantDTO + let variantTwo: ProductVariantDTO + let productOne: ProductDTO + let productTwo: ProductDTO beforeEach(async () => { productOne = await service.create({ @@ -34,26 +54,28 @@ moduleIntegrationTestRunner({ values: ["red", "blue"], }, ], - }) + } as CreateProductDTO) productTwo = await service.create({ id: "product-2", title: "product 2", status: ProductStatus.PUBLISHED, - }) + } as CreateProductDTO) variantOne = await service.createVariants({ id: "test-1", title: "variant 1", product_id: productOne.id, options: { size: "large" }, - }) + } as CreateProductVariantDTO) variantTwo = await service.createVariants({ id: "test-2", title: "variant", product_id: productTwo.id, - }) + } as CreateProductVariantDTO) + + jest.clearAllMocks() }) describe("listAndCountVariants", () => { @@ -183,6 +205,16 @@ moduleIntegrationTestRunner({ const productVariant = await service.retrieveVariant(variantOne.id) expect(productVariant.title).toEqual("new test") + + expect(eventBusEmitSpy.mock.calls[0][0]).toHaveLength(1) + expect(eventBusEmitSpy).toHaveBeenCalledWith([ + composeMessage(ProductEvents.product_variant_updated, { + data: { id: variantOne.id }, + object: "product_variant", + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + }), + ]) }) it("should upsert the options of a variant successfully", async () => { @@ -203,6 +235,16 @@ moduleIntegrationTestRunner({ }), ]) ) + + expect(eventBusEmitSpy.mock.calls[0][0]).toHaveLength(1) + expect(eventBusEmitSpy).toHaveBeenCalledWith([ + composeMessage(ProductEvents.product_variant_updated, { + data: { id: variantOne.id }, + object: "product_variant", + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + }), + ]) }) it("should do a partial update on the options of a variant successfully", async () => { @@ -241,6 +283,42 @@ moduleIntegrationTestRunner({ }) }) + describe("createVariants", () => { + it("should create variants successfully", async () => { + jest.clearAllMocks() + + const data: CreateProductVariantDTO = { + title: "variant 3", + product_id: productOne.id, + options: { size: "small" }, + } + + const variant = await service.createVariants(data) + + expect(variant).toEqual( + expect.objectContaining({ + title: "variant 3", + product_id: productOne.id, + options: expect.arrayContaining([ + expect.objectContaining({ + value: "small", + }), + ]), + }) + ) + + expect(eventBusEmitSpy.mock.calls[0][0]).toHaveLength(1) + expect(eventBusEmitSpy).toHaveBeenCalledWith([ + composeMessage(ProductEvents.product_variant_created, { + data: { id: variant.id }, + object: "product_variant", + service: Modules.PRODUCT, + action: CommonEvents.CREATED, + }), + ]) + }) + }) + describe("softDelete variant", () => { it("should soft delete a variant and its relations", async () => { const beforeDeletedVariants = await service.listVariants( diff --git a/packages/modules/product/integration-tests/__tests__/services/product-module-service/products.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts similarity index 98% rename from packages/modules/product/integration-tests/__tests__/services/product-module-service/products.spec.ts rename to packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts index fd8789c835..a4480ab5d8 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product-module-service/products.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts @@ -12,23 +12,22 @@ import { UpdateProductInput } from "@types" import { MockEventBusService, moduleIntegrationTestRunner, - SuiteOptions, } from "medusa-test-utils" -import { createCollections, createTypes } from "../../../__fixtures__/product" -import { createProductCategories } from "../../../__fixtures__/product-category" -import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product" +import { + buildProductAndRelationsData, + createCollections, + createTypes, +} from "../../__fixtures__/product" +import { createProductCategories } from "../../__fixtures__/product-category" jest.setTimeout(300000) -moduleIntegrationTestRunner({ +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, injectedDependencies: { eventBusModuleService: new MockEventBusService(), }, - testSuite: ({ - MikroOrmWrapper, - service, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service }) => { describe("ProductModuleService products", function () { let productCollectionOne: ProductCollection let productCollectionTwo: ProductCollection diff --git a/packages/modules/product/integration-tests/__tests__/services/product/index.ts b/packages/modules/product/integration-tests/__tests__/product.ts similarity index 97% rename from packages/modules/product/integration-tests/__tests__/services/product/index.ts rename to packages/modules/product/integration-tests/__tests__/product.ts index 88d1a38965..e705d9dea4 100644 --- a/packages/modules/product/integration-tests/__tests__/services/product/index.ts +++ b/packages/modules/product/integration-tests/__tests__/product.ts @@ -6,33 +6,34 @@ import { createImages, createProductAndTags, createProductVariants, -} from "../../../__fixtures__/product" +} from "../__fixtures__/product" import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService, ProductDTO } from "@medusajs/types" -import { ProductStatus, kebabCase } from "@medusajs/utils" +import { kebabCase, ProductStatus } from "@medusajs/utils" import { SqlEntityManager } from "@mikro-orm/postgresql" import { ProductService } from "@services" -import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils" -import { createProductCategories } from "../../../__fixtures__/product-category" +import { moduleIntegrationTestRunner } from "medusa-test-utils" +import { createProductCategories } from "../__fixtures__/product-category" import { categoriesData, productsData, variantsData, -} from "../../../__fixtures__/product/data" +} from "../__fixtures__/product/data" jest.setTimeout(30000) -moduleIntegrationTestRunner({ +type Service = IProductModuleService & { + productService_: ProductService +} + +moduleIntegrationTestRunner({ moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { + testSuite: ({ MikroOrmWrapper, service: moduleService }) => { let service: ProductService beforeEach(() => { - service = medusaApp.modules["productService"].productService_ + service = moduleService.productService_ }) describe("Product Service", () => { diff --git a/packages/modules/product/integration-tests/__tests__/services/product-collection/index.ts b/packages/modules/product/integration-tests/__tests__/services/product-collection/index.ts deleted file mode 100644 index a1e62b4b2b..0000000000 --- a/packages/modules/product/integration-tests/__tests__/services/product-collection/index.ts +++ /dev/null @@ -1,370 +0,0 @@ -import { ProductCollectionService } from "@services" - -import { createCollections } from "../../../__fixtures__/product" -import { Modules } from "@medusajs/modules-sdk" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -import { IProductModuleService } from "@medusajs/types" - -jest.setTimeout(30000) - -moduleIntegrationTestRunner({ - moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { - describe("Product collection Service", () => { - let service: ProductCollectionService - - beforeEach(() => { - service = medusaApp.modules["productService"].productCollectionService_ - }) - - describe("list", () => { - const data = [ - { - id: "test-1", - title: "col 1", - }, - { - id: "test-2", - title: "col 2", - }, - { - id: "test-3", - title: "col 3 extra", - }, - { - id: "test-4", - title: "col 4 extra", - }, - ] - - beforeEach(async () => { - await createCollections(MikroOrmWrapper.forkManager(), data) - }) - - it("list product collections", async () => { - const productCollectionResults = await service.list() - - expect(productCollectionResults).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - expect.objectContaining({ - id: "test-2", - title: "col 2", - }), - expect.objectContaining({ - id: "test-3", - title: "col 3 extra", - }), - expect.objectContaining({ - id: "test-4", - title: "col 4 extra", - }), - ]) - }) - - it("list product collections by id", async () => { - const productCollectionResults = await service.list({ - id: data![0].id, - }) - - expect(productCollectionResults).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - ]) - }) - - it("list product collections by title matching string", async () => { - const productCollectionResults = await service.list({ - title: "col 3 extra", - }) - - expect(productCollectionResults).toEqual([ - expect.objectContaining({ - id: "test-3", - title: "col 3 extra", - }), - ]) - }) - }) - - describe("listAndCount", () => { - const data = [ - { - id: "test-1", - title: "col 1", - }, - { - id: "test-2", - title: "col 2", - }, - { - id: "test-3", - title: "col 3 extra", - }, - { - id: "test-4", - title: "col 4 extra", - }, - ] - - beforeEach(async () => { - await createCollections(MikroOrmWrapper.forkManager(), data) - }) - - it("should return all collections and count", async () => { - const [productCollectionResults, count] = await service.listAndCount() - const serialized = JSON.parse( - JSON.stringify(productCollectionResults) - ) - - expect(serialized).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - expect.objectContaining({ - id: "test-2", - title: "col 2", - }), - expect.objectContaining({ - id: "test-3", - title: "col 3 extra", - }), - expect.objectContaining({ - id: "test-4", - title: "col 4 extra", - }), - ]) - }) - - it("should return count and collections based on filter data", async () => { - const [productCollectionResults, count] = await service.listAndCount({ - id: data![0].id, - }) - const serialized = JSON.parse( - JSON.stringify(productCollectionResults) - ) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - ]) - }) - - it("should return count and collections based on config data", async () => { - const [productCollectionResults, count] = await service.listAndCount( - {}, - { - relations: ["products"], - select: ["title"], - take: 1, - skip: 1, - } - ) - const serialized = JSON.parse( - JSON.stringify(productCollectionResults) - ) - - expect(count).toEqual(4) - expect(serialized).toEqual([ - { - id: "test-2", - title: "col 2", - handle: "col-2", - products: [], - }, - ]) - }) - }) - - describe("retrieve", () => { - const collectionData = { - id: "collection-1", - title: "collection 1", - } - - beforeEach(async () => { - await createCollections(MikroOrmWrapper.forkManager(), [ - collectionData, - ]) - }) - - it("should return collection for the given id", async () => { - const productCollectionResults = await service.retrieve( - collectionData.id - ) - - expect(productCollectionResults).toEqual( - expect.objectContaining({ - id: collectionData.id, - }) - ) - }) - - it("should throw an error when collection with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductCollection with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "productCollection - id must be defined" - ) - }) - - it("should return collection based on config select param", async () => { - const productCollectionResults = await service.retrieve( - collectionData.id, - { - select: ["id", "title"], - } - ) - - const serialized = JSON.parse( - JSON.stringify(productCollectionResults) - ) - - expect(serialized).toEqual({ - id: collectionData.id, - title: collectionData.title, - handle: "collection-1", - }) - }) - - it("should return collection based on config relation param", async () => { - const productCollectionResults = await service.retrieve( - collectionData.id, - { - select: ["id", "title"], - relations: ["products"], - } - ) - - const serialized = JSON.parse( - JSON.stringify(productCollectionResults) - ) - - expect(serialized).toEqual({ - id: collectionData.id, - title: collectionData.title, - handle: "collection-1", - products: [], - }) - }) - }) - - describe("delete", () => { - const collectionId = "collection-1" - const collectionData = { - id: collectionId, - title: "collection 1", - } - - beforeEach(async () => { - await createCollections(MikroOrmWrapper.forkManager(), [ - collectionData, - ]) - }) - - it("should delete the product collection given an ID successfully", async () => { - await service.delete([collectionId]) - - const collections = await service.list({ - id: collectionId, - }) - - expect(collections).toHaveLength(0) - }) - }) - - describe("update", () => { - const collectionId = "collection-1" - const collectionData = { - id: collectionId, - title: "collection 1", - } - - beforeEach(async () => { - await createCollections(MikroOrmWrapper.forkManager(), [ - collectionData, - ]) - }) - - it("should update the value of the collection successfully", async () => { - await service.update([ - { - id: collectionId, - title: "New Collection", - }, - ]) - - const productCollection = await service.retrieve(collectionId) - - expect(productCollection.title).toEqual("New Collection") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - title: "New Collection", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductCollection with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a collection successfully", async () => { - await service.create([ - { - title: "New Collection", - }, - ]) - - const [productCollection] = await service.list({ - title: "New Collection", - }) - - expect(productCollection.title).toEqual("New Collection") - }) - }) - }) - }, -}) diff --git a/packages/modules/product/integration-tests/__tests__/services/product-option/index.ts b/packages/modules/product/integration-tests/__tests__/services/product-option/index.ts deleted file mode 100644 index 28ef7ab286..0000000000 --- a/packages/modules/product/integration-tests/__tests__/services/product-option/index.ts +++ /dev/null @@ -1,369 +0,0 @@ -import { Modules } from "@medusajs/modules-sdk" -import { IProductModuleService } from "@medusajs/types" -import { ProductStatus } from "@medusajs/utils" -import { Product } from "@models" -import { ProductOptionService } from "@services" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -import { createOptions } from "../../../__fixtures__/product" - -jest.setTimeout(30000) - -moduleIntegrationTestRunner({ - moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { - describe("ProductOption Service", () => { - let service: ProductOptionService - - beforeEach(() => { - service = medusaApp.modules["productService"].productOptionService_ - }) - - let productOne: Product - let productTwo: Product - - const productOneData = { - id: "product-1", - title: "product 1", - status: ProductStatus.PUBLISHED, - } - - const productTwoData = { - id: "product-2", - title: "product 2", - status: ProductStatus.PUBLISHED, - } - - beforeEach(async () => { - const testManager = MikroOrmWrapper.forkManager() - productOne = testManager.create(Product, productOneData) - productTwo = testManager.create(Product, productTwoData) - - await createOptions(testManager, [ - { - id: "option-1", - title: "Option 1", - product: productOne, - }, - { - id: "option-2", - title: "Option 2", - product: productOne, - }, - ]) - }) - - describe("list", () => { - it("list product option", async () => { - const optionResults = await service.list() - - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-1", - title: "Option 1", - }), - expect.objectContaining({ - id: "option-2", - title: "Option 2", - }), - ]) - }) - - it("list product option by id", async () => { - const optionResults = await service.list({ id: "option-2" }) - - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-2", - title: "Option 2", - }), - ]) - }) - - it("list product option by title matching string", async () => { - const optionResults = await service.list({ title: "Option 1" }) - - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-1", - title: "Option 1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return product option and count", async () => { - const [optionResults, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-1", - title: "Option 1", - }), - expect.objectContaining({ - id: "option-2", - title: "Option 2", - }), - ]) - }) - - it("should return product option and count when filtered", async () => { - const [optionResults, count] = await service.listAndCount({ - id: "option-2", - }) - - expect(count).toEqual(1) - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-2", - }), - ]) - }) - - it("should return product option and count when using skip and take", async () => { - const [optionResults, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [optionResults, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["title"], - } - ) - - const serialized = JSON.parse(JSON.stringify(optionResults)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "option-1", - }), - ]) - }) - }) - - describe("retrieve", () => { - const optionId = "option-1" - const optionValue = "Option 1" - - it("should return option for the given id", async () => { - const option = await service.retrieve(optionId) - - expect(option).toEqual( - expect.objectContaining({ - id: optionId, - }) - ) - }) - - it("should throw an error when option with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductOption with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productOption - id must be defined") - }) - - it("should return option based on config select param", async () => { - const option = await service.retrieve(optionId, { - select: ["id", "title"], - }) - - const serialized = JSON.parse(JSON.stringify(option)) - - expect(serialized).toEqual({ - id: optionId, - title: optionValue, - product_id: null, - }) - }) - }) - - describe("delete", () => { - const optionId = "option-1" - - it("should delete the product option given an ID successfully", async () => { - await service.delete([optionId]) - - const options = await service.list({ - id: optionId, - }) - - expect(options).toHaveLength(0) - }) - }) - - describe("update", () => { - const optionId = "option-1" - - it("should update the title of the option successfully", async () => { - await service.update([ - { - id: optionId, - title: "UK", - }, - ]) - - const productOption = await service.retrieve(optionId) - - expect(productOption.title).toEqual("UK") - }) - - it("should update the relationship of the option successfully", async () => { - await service.update([ - { - id: optionId, - product_id: productTwo.id, - }, - ]) - - const productOption = await service.retrieve(optionId, { - relations: ["product"], - }) - - expect(productOption).toEqual( - expect.objectContaining({ - id: optionId, - product: expect.objectContaining({ - id: productTwo.id, - }), - }) - ) - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - title: "UK", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductOption with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a option successfully", async () => { - await service.create([ - { - title: "UK", - product: productOne, - }, - ]) - - const [productOption] = await service.list( - { - title: "UK", - }, - { - relations: ["product"], - } - ) - - expect(productOption).toEqual( - expect.objectContaining({ - title: "UK", - product: expect.objectContaining({ - id: productOne.id, - }), - }) - ) - }) - }) - - describe("upsert", function () { - it("should create an option and update another option successfully", async () => { - const productOption = ( - await service.create([ - { - title: "UK", - product: productOne, - }, - ]) - )[0] - - const optionToUpdate = { - id: productOption.id, - title: "US", - } - - const newOption = { - title: "US2", - product_id: productOne.id, - } - - await service.upsert([optionToUpdate, newOption]) - - const productOptions = await service.list( - { - q: "US%", - }, - { - relations: ["product"], - } - ) - - expect(JSON.parse(JSON.stringify(productOptions))).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - title: "US", - product: expect.objectContaining({ - id: productOne.id, - }), - }), - expect.objectContaining({ - title: newOption.title, - product: expect.objectContaining({ - id: productOne.id, - }), - }), - ]) - ) - }) - }) - }) - }, -}) diff --git a/packages/modules/product/integration-tests/__tests__/services/product-tag/index.ts b/packages/modules/product/integration-tests/__tests__/services/product-tag/index.ts deleted file mode 100644 index 0b0ba06d50..0000000000 --- a/packages/modules/product/integration-tests/__tests__/services/product-tag/index.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { Modules } from "@medusajs/modules-sdk" -import { IProductModuleService, ModulesSdkTypes } from "@medusajs/types" -import { ProductStatus } from "@medusajs/utils" -import { Product, ProductTag } from "@models" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -import { createProductAndTags } from "../../../__fixtures__/product" - -jest.setTimeout(30000) - -moduleIntegrationTestRunner({ - moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { - describe("ProductTag Service", () => { - let data!: Product[] - let service: ModulesSdkTypes.InternalModuleService - - beforeEach(() => { - service = medusaApp.modules["productService"].productTagService_ - }) - - const productsData = [ - { - id: "test-1", - title: "product 1", - status: ProductStatus.PUBLISHED, - tags: [ - { - id: "tag-1", - value: "France", - }, - ], - }, - { - id: "test-2", - title: "product", - status: ProductStatus.PUBLISHED, - tags: [ - { - id: "tag-2", - value: "Germany", - }, - { - id: "tag-3", - value: "United States", - }, - { - id: "tag-4", - value: "United Kingdom", - }, - ], - }, - ] - - beforeEach(async () => { - data = await createProductAndTags( - MikroOrmWrapper.forkManager(), - productsData - ) - }) - - describe("list", () => { - it("list product tags", async () => { - const tagsResults = await service.list() - - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - value: "France", - }), - expect.objectContaining({ - id: "tag-2", - value: "Germany", - }), - expect.objectContaining({ - id: "tag-3", - value: "United States", - }), - expect.objectContaining({ - id: "tag-4", - value: "United Kingdom", - }), - ]) - }) - - it("list product tags by id", async () => { - const tagsResults = await service.list({ id: data[0].tags![0].id }) - - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - value: "France", - }), - ]) - }) - - it("list product tags by value matching string", async () => { - const tagsResults = await service.list({ q: "united kingdom" }) - - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-4", - value: "United Kingdom", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return product tags and count", async () => { - const [tagsResults, count] = await service.listAndCount() - - expect(count).toEqual(4) - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - value: "France", - }), - expect.objectContaining({ - id: "tag-2", - value: "Germany", - }), - expect.objectContaining({ - id: "tag-3", - value: "United States", - }), - expect.objectContaining({ - id: "tag-4", - value: "United Kingdom", - }), - ]) - }) - - it("should return product tags and count when filtered", async () => { - const [tagsResults, count] = await service.listAndCount({ - id: data[0].tags![0].id, - }) - - expect(count).toEqual(1) - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - }), - ]) - }) - - it("should return product tags and count when using skip and take", async () => { - const [tagsResults, count] = await service.listAndCount( - {}, - { skip: 1, take: 2 } - ) - - expect(count).toEqual(4) - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-2", - }), - expect.objectContaining({ - id: "tag-3", - }), - ]) - }) - - it("should return requested fields and relations", async () => { - const [tagsResults, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["value", "products.id"], - relations: ["products"], - } - ) - - const serialized = JSON.parse(JSON.stringify(tagsResults)) - - expect(count).toEqual(4) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "tag-1", - products: [ - { - id: "test-1", - collection_id: null, - type_id: null, - }, - ], - value: "France", - }), - ]) - }) - }) - - describe("retrieve", () => { - const tagId = "tag-1" - const tagValue = "France" - const productId = "test-1" - - it("should return tag for the given id", async () => { - const tag = await service.retrieve(tagId) - - expect(tag).toEqual( - expect.objectContaining({ - id: tagId, - }) - ) - }) - - it("should throw an error when tag with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductTag with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productTag - id must be defined") - }) - - it("should return tag based on config select param", async () => { - const tag = await service.retrieve(tagId, { - select: ["id", "value"], - }) - - const serialized = JSON.parse(JSON.stringify(tag)) - - expect(serialized).toEqual({ - id: tagId, - value: tagValue, - }) - }) - - it("should return tag based on config relation param", async () => { - const tag = await service.retrieve(tagId, { - select: ["id", "value", "products.id"], - relations: ["products"], - }) - - const serialized = JSON.parse(JSON.stringify(tag)) - - expect(serialized).toEqual({ - id: tagId, - value: tagValue, - products: [ - expect.objectContaining({ - id: productId, - }), - ], - }) - }) - }) - - describe("delete", () => { - const tagId = "tag-1" - - it("should delete the product tag given an ID successfully", async () => { - await service.delete([tagId]) - - const tags = await service.list({ - id: tagId, - }) - - expect(tags).toHaveLength(0) - }) - }) - - describe("update", () => { - const tagId = "tag-1" - - it("should update the value of the tag successfully", async () => { - await service.update([ - { - id: tagId, - value: "UK", - }, - ]) - - const productTag = await service.retrieve(tagId) - - expect(productTag.value).toEqual("UK") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - value: "UK", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductTag with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a tag successfully", async () => { - await service.create([ - { - value: "UK", - }, - ]) - - const [productTag] = await service.list({ - value: "UK", - }) - - expect(productTag.value).toEqual("UK") - }) - }) - }) - }, -}) diff --git a/packages/modules/product/integration-tests/__tests__/services/product-type/index.ts b/packages/modules/product/integration-tests/__tests__/services/product-type/index.ts deleted file mode 100644 index 6a1c9d9f6f..0000000000 --- a/packages/modules/product/integration-tests/__tests__/services/product-type/index.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { Modules } from "@medusajs/modules-sdk" -import { IProductModuleService } from "@medusajs/types" -import { ProductStatus } from "@medusajs/utils" -import { Product } from "@models" -import { ProductTypeService } from "@services" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -import { createProductAndTypes } from "../../../__fixtures__/product" - -jest.setTimeout(30000) - -moduleIntegrationTestRunner({ - moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { - describe("ProductType Service", () => { - let data!: Product[] - let service: ProductTypeService - - beforeEach(() => { - service = medusaApp.modules["productService"].productTypeService_ - }) - - const productsData = [ - { - id: "product-1", - title: "product 1", - status: ProductStatus.PUBLISHED, - type: { - id: "type-1", - value: "Type 1", - }, - }, - { - id: "product-2", - title: "product", - status: ProductStatus.PUBLISHED, - type: { - id: "type-2", - value: "Type 2", - }, - }, - ] - - beforeEach(async () => { - data = await createProductAndTypes( - MikroOrmWrapper.forkManager(), - productsData - ) - }) - describe("list", () => { - it("list product type", async () => { - const typeResults = await service.list() - - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - expect.objectContaining({ - id: "type-2", - value: "Type 2", - }), - ]) - }) - - it("list product type by id", async () => { - const typeResults = await service.list({ id: data[0].type.id }) - - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - ]) - }) - - it("list product type by value matching string", async () => { - const typeResults = await service.list({ value: "Type 1" }) - - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return product type and count", async () => { - const [typeResults, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - expect.objectContaining({ - id: "type-2", - value: "Type 2", - }), - ]) - }) - - it("should return product type and count when filtered", async () => { - const [typeResults, count] = await service.listAndCount({ - id: data[0].type.id, - }) - - expect(count).toEqual(1) - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - }), - ]) - }) - - it("should return product type and count when using skip and take", async () => { - const [typeResults, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [typeResults, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["value"], - } - ) - - const serialized = JSON.parse(JSON.stringify(typeResults)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "type-1", - }), - ]) - }) - }) - - describe("retrieve", () => { - const typeId = "type-1" - const typeValue = "Type 1" - - it("should return type for the given id", async () => { - const type = await service.retrieve(typeId) - - expect(type).toEqual( - expect.objectContaining({ - id: typeId, - }) - ) - }) - - it("should throw an error when type with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductType with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productType - id must be defined") - }) - - it("should return type based on config select param", async () => { - const type = await service.retrieve(typeId, { - select: ["id", "value"], - }) - - const serialized = JSON.parse(JSON.stringify(type)) - - expect(serialized).toEqual({ - id: typeId, - value: typeValue, - }) - }) - }) - - describe("delete", () => { - const typeId = "type-1" - - it("should delete the product type given an ID successfully", async () => { - await service.delete([typeId]) - - const types = await service.list({ - id: typeId, - }) - - expect(types).toHaveLength(0) - }) - }) - - describe("update", () => { - const typeId = "type-1" - - it("should update the value of the type successfully", async () => { - await service.update([ - { - id: typeId, - value: "UK", - }, - ]) - - const productType = await service.retrieve(typeId) - - expect(productType.value).toEqual("UK") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - value: "UK", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductType with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a type successfully", async () => { - await service.create([ - { - value: "UK", - }, - ]) - - const [productType] = await service.list({ - value: "UK", - }) - - expect(productType.value).toEqual("UK") - }) - }) - }) - }, -}) diff --git a/packages/modules/product/integration-tests/__tests__/services/product-variant/index.ts b/packages/modules/product/integration-tests/__tests__/services/product-variant/index.ts deleted file mode 100644 index 5f48a944df..0000000000 --- a/packages/modules/product/integration-tests/__tests__/services/product-variant/index.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { InternalModuleService, IProductModuleService } from "@medusajs/types" -import { ProductStatus } from "@medusajs/utils" -import { Collection } from "@mikro-orm/core" -import { Product, ProductOption, ProductTag, ProductVariant } from "@models" -import { - createOptions, - createProductAndTags, - createProductVariants, -} from "../../../__fixtures__/product" -import { productsData, variantsData } from "../../../__fixtures__/product/data" -import { buildProductVariantOnlyData } from "../../../__fixtures__/variant/data/create-variant" - -import { Modules } from "@medusajs/modules-sdk" -import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" - -jest.setTimeout(30000) - -moduleIntegrationTestRunner({ - moduleName: Modules.PRODUCT, - testSuite: ({ - MikroOrmWrapper, - medusaApp, - }: SuiteOptions) => { - describe.skip("ProductVariant Service", () => { - let variantOne: ProductVariant - let variantTwo: ProductVariant - let productOne: Product - const productVariantTestOne = "test-1" - let service: InternalModuleService - - beforeEach(() => { - service = medusaApp.modules["productService"].productVariantService_ - }) - - describe("list", () => { - beforeEach(async () => { - const testManager = await MikroOrmWrapper.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductStatus.PUBLISHED, - }) - - variantOne = testManager.create(ProductVariant, { - id: productVariantTestOne, - title: "variant 1", - product: productOne, - }) - - variantTwo = testManager.create(ProductVariant, { - id: "test-2", - title: "variant", - product: productOne, - }) - - await testManager.persistAndFlush([variantOne, variantTwo]) - }) - - it("selecting by properties, scopes out the results", async () => { - const results = await service.list({ - id: variantOne.id, - }) - - expect(results).toEqual([ - expect.objectContaining({ - id: variantOne.id, - title: "variant 1", - }), - ]) - }) - - it("passing a limit, scopes the result to the limit", async () => { - const results = await service.list( - {}, - { - take: 1, - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: variantOne.id, - }), - ]) - }) - - it("passing populate, scopes the results of the response", async () => { - const results = await service.list( - { - id: productVariantTestOne, - }, - { - select: ["id", "title", "product.title"] as any, - relations: ["product"], - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: productVariantTestOne, - title: "variant 1", - product: expect.objectContaining({ - id: "product-1", - title: "product 1", - tags: expect.any(Collection), - variants: expect.any(Collection), - }), - }), - ]) - - expect(JSON.parse(JSON.stringify(results))).toEqual([ - { - id: productVariantTestOne, - title: "variant 1", - product: { - id: "product-1", - title: "product 1", - }, - }, - ]) - }) - }) - - describe("relation: options", () => { - let products: Product[] - let variants: ProductVariant[] - let options: ProductOption[] - - beforeEach(async () => { - const testManager = await MikroOrmWrapper.forkManager() - - products = (await createProductAndTags( - testManager, - productsData - )) as Product[] - variants = (await createProductVariants( - testManager, - variantsData - )) as ProductVariant[] - - options = await createOptions(testManager, [ - { - id: "test-option-1", - title: "size", - product: products[0], - values: [ - { - id: "value-1", - value: "XS", - variant: products[0].variants[0], - }, - { - id: "value-1", - value: "XL", - variant: products[0].variants[0], - }, - ], - }, - { - id: "test-option-2", - title: "color", - product: products[0], - value: "blue", - variant: products[0].variants[0], - }, - ]) - }) - - it("filter by options relation", async () => { - const variants = await service.list( - { options: { id: ["value-1"] } }, - { relations: ["options"] } - ) - - expect(JSON.parse(JSON.stringify(variants))).toEqual([ - expect.objectContaining({ - id: productVariantTestOne, - title: "variant title", - sku: "sku 1", - }), - ]) - }) - }) - - describe("create", function () { - let products: Product[] - let productOptions!: ProductOption[] - - beforeEach(async () => { - const testManager = await MikroOrmWrapper.forkManager() - - products = (await createProductAndTags( - testManager, - productsData - )) as Product[] - - productOptions = await createOptions(testManager, [ - { - id: "test-option-1", - title: "size", - product: products[0], - }, - ]) - }) - - it("should create a variant", async () => { - const data = buildProductVariantOnlyData({ - options: [ - { - option: productOptions[0], - value: "XS", - }, - ], - }) - - const variants = await service.create(products[0].id, [data]) - - expect(variants).toHaveLength(1) - expect(variants[0].options).toHaveLength(1) - - expect(JSON.parse(JSON.stringify(variants[0]))).toEqual( - expect.objectContaining({ - id: expect.any(String), - title: data.title, - sku: data.sku, - allow_backorder: false, - manage_inventory: true, - variant_rank: 0, - product: expect.objectContaining({ - id: products[0].id, - }), - options: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: data.options![0].value, - }), - ]), - }) - ) - }) - }) - - describe("retrieve", () => { - beforeEach(async () => { - const testManager = await MikroOrmWrapper.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductStatus.PUBLISHED, - }) - - variantOne = testManager.create(ProductVariant, { - id: productVariantTestOne, - title: "variant 1", - product: productOne, - }) - - await testManager.persistAndFlush([variantOne]) - }) - - it("should return the requested variant", async () => { - const result = await service.retrieve(variantOne.id) - - expect(result).toEqual( - expect.objectContaining({ - id: productVariantTestOne, - title: "variant 1", - }) - ) - }) - - it("should return requested attributes when requested through config", async () => { - const result = await service.retrieve(variantOne.id, { - select: ["id", "title", "product.title"] as any, - relations: ["product"], - }) - - expect(result).toEqual( - expect.objectContaining({ - id: productVariantTestOne, - title: "variant 1", - product_id: "product-1", - product: expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - }) - ) - }) - - it("should throw an error when a variant with ID does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductVariant with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productVariant - id must be defined") - }) - }) - }) - }, -}) diff --git a/packages/modules/product/src/services/product-module-service.ts b/packages/modules/product/src/services/product-module-service.ts index 471f03e932..5f1f84cced 100644 --- a/packages/modules/product/src/services/product-module-service.ts +++ b/packages/modules/product/src/services/product-module-service.ts @@ -22,6 +22,7 @@ import { ProductCategoryService, ProductService } from "@services" import { arrayDifference, + EmitEvents, InjectManager, InjectTransactionManager, isPresent, @@ -51,6 +52,7 @@ import { UpdateTypeInput, } from "../types" import { entityNameToLinkableKeysMap, joinerConfig } from "./../joiner-config" +import { eventBuilders } from "../utils" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -128,7 +130,6 @@ export default class ProductModuleService< protected readonly productService_: ProductService // eslint-disable-next-line max-len protected readonly productVariantService_: ModulesSdkTypes.InternalModuleService - // eslint-disable-next-line max-len protected readonly productCategoryService_: ProductCategoryService // eslint-disable-next-line max-len @@ -193,6 +194,7 @@ export default class ProductModuleService< ): Promise @InjectManager("baseRepository_") + @EmitEvents() async createVariants( data: | ProductTypes.CreateProductVariantDTO[] @@ -220,7 +222,7 @@ export default class ProductModuleService< if (data.some((v) => !v.product_id)) { throw new MedusaError( MedusaError.Types.INVALID_DATA, - "Tried to create variants without specifying a product_id" + "Unable to create variants without specifying a product_id" ) } @@ -238,10 +240,17 @@ export default class ProductModuleService< const productVariantsWithOptions = ProductModuleService.assignOptionsToVariants(data, productOptions) - return await this.productVariantService_.create( + const createdVariants = await this.productVariantService_.create( productVariantsWithOptions, sharedContext ) + + eventBuilders.createdProductVariant({ + data: createdVariants, + sharedContext, + }) + + return createdVariants } async upsertVariants( @@ -254,6 +263,7 @@ export default class ProductModuleService< ): Promise @InjectTransactionManager("baseRepository_") + @EmitEvents() async upsertVariants( data: | ProductTypes.UpsertProductVariantDTO[] @@ -300,6 +310,7 @@ export default class ProductModuleService< ): Promise @InjectManager("baseRepository_") + @EmitEvents() async updateVariants( idOrSelector: string | ProductTypes.FilterableProductVariantProps, data: ProductTypes.UpdateProductVariantDTO, @@ -373,7 +384,7 @@ export default class ProductModuleService< sharedContext ) - const { entities: productVariants } = + const { entities: productVariants, performedActions } = await this.productVariantService_.upsertWithReplace( ProductModuleService.assignOptionsToVariants( variantsWithProductId, @@ -385,6 +396,19 @@ export default class ProductModuleService< sharedContext ) + eventBuilders.createdProductVariant({ + data: performedActions.created[ProductVariant.name] ?? [], + sharedContext, + }) + eventBuilders.updatedProductVariant({ + data: performedActions.updated[ProductVariant.name] ?? [], + sharedContext, + }) + eventBuilders.deletedProductVariant({ + data: performedActions.deleted[ProductVariant.name] ?? [], + sharedContext, + }) + return productVariants } diff --git a/packages/modules/product/src/types/index.ts b/packages/modules/product/src/types/index.ts index 04b25cfb11..11837ba971 100644 --- a/packages/modules/product/src/types/index.ts +++ b/packages/modules/product/src/types/index.ts @@ -5,6 +5,8 @@ export type InitializeModuleInjectableDependencies = { eventBusModuleService?: IEventBusModuleService } +// TODO: remove and cleanup bellow code + export type ProductCategoryEventData = { id: string } diff --git a/packages/modules/product/src/utils/events.ts b/packages/modules/product/src/utils/events.ts new file mode 100644 index 0000000000..81ecb0f21b --- /dev/null +++ b/packages/modules/product/src/utils/events.ts @@ -0,0 +1,102 @@ +import { + CommonEvents, + eventBuilderFactory, + Modules, + ProductEvents, +} from "@medusajs/utils" + +export const eventBuilders = { + createdProduct: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.CREATED, + object: "product", + eventsEnum: ProductEvents, + isMainEntity: true, + }), + updatedProduct: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + object: "product", + eventsEnum: ProductEvents, + isMainEntity: true, + }), + deletedProduct: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.DELETED, + object: "product", + eventsEnum: ProductEvents, + isMainEntity: true, + }), + createdProductVariant: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.CREATED, + object: "product_variant", + eventsEnum: ProductEvents, + }), + updatedProductVariant: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + object: "product_variant", + eventsEnum: ProductEvents, + }), + deletedProductVariant: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.DELETED, + object: "product_variant", + eventsEnum: ProductEvents, + }), + createdProductOption: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.CREATED, + object: "product_option", + eventsEnum: ProductEvents, + }), + updatedProductOption: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + object: "product_option", + eventsEnum: ProductEvents, + }), + deletedProductOption: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.DELETED, + object: "product_option", + eventsEnum: ProductEvents, + }), + createdProductType: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.CREATED, + object: "product_type", + eventsEnum: ProductEvents, + }), + updatedProductType: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + object: "product_type", + eventsEnum: ProductEvents, + }), + deletedProductType: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.DELETED, + object: "product_type", + eventsEnum: ProductEvents, + }), + createdProductTag: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.CREATED, + object: "product_tag", + eventsEnum: ProductEvents, + }), + updatedProductTag: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.UPDATED, + object: "product_tag", + eventsEnum: ProductEvents, + }), + deletedProductTag: eventBuilderFactory({ + service: Modules.PRODUCT, + action: CommonEvents.DELETED, + object: "product_tag", + eventsEnum: ProductEvents, + }), +} diff --git a/packages/modules/product/src/utils/index.ts b/packages/modules/product/src/utils/index.ts new file mode 100644 index 0000000000..92c2484024 --- /dev/null +++ b/packages/modules/product/src/utils/index.ts @@ -0,0 +1 @@ +export * from "./events"