From f4f6bb3a813ed0b532a7f729857b2025ee7d3b42 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 19 Oct 2023 11:51:40 +0200 Subject: [PATCH 01/49] feat: sales channel joiner config --- packages/medusa/src/joiner-configs/index.ts | 1 + .../joiner-configs/sales-channel-service.ts | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 packages/medusa/src/joiner-configs/sales-channel-service.ts diff --git a/packages/medusa/src/joiner-configs/index.ts b/packages/medusa/src/joiner-configs/index.ts index 4e41101c47355..084dc203f659d 100644 --- a/packages/medusa/src/joiner-configs/index.ts +++ b/packages/medusa/src/joiner-configs/index.ts @@ -1,4 +1,5 @@ export * as cart from "./cart-service" export * as customer from "./customer-service" export * as region from "./region-service" +export * as salesChannel from "./sales-channel-service" export * as shippingProfile from "./shipping-profile-service" diff --git a/packages/medusa/src/joiner-configs/sales-channel-service.ts b/packages/medusa/src/joiner-configs/sales-channel-service.ts new file mode 100644 index 0000000000000..401499f1f8080 --- /dev/null +++ b/packages/medusa/src/joiner-configs/sales-channel-service.ts @@ -0,0 +1,30 @@ +import { ModuleJoinerConfig } from "@medusajs/types" + +export default { + serviceName: "salesChannelService", + primaryKeys: ["id"], + linkableKeys: { sales_channel: "SalesChannel" }, + schema: ` + scalar Date + scalar JSON + + type SalesChannel { + id: ID! + name: String! + description: String! + is_disabled: Boolean! + created_at: Date! + updated_at: Date! + deleted_at: Date + metadata: JSON + } + `, + alias: [ + { + name: "sales_channel", + }, + { + name: "sales_channels", + }, + ], +} as ModuleJoinerConfig From 4c6ebafae5096916fb8340f03b6bfca56f820740 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 23 Oct 2023 11:17:20 +0200 Subject: [PATCH 02/49] feat: product sales channel link config, SC list method --- .../link-modules/src/definitions/index.ts | 1 + .../src/definitions/product-sales-channel.ts | 64 +++++++++++++++++++ packages/link-modules/src/links.ts | 6 ++ .../joiner-configs/sales-channel-service.ts | 6 +- packages/medusa/src/services/sales-channel.ts | 18 ++++++ 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 packages/link-modules/src/definitions/product-sales-channel.ts diff --git a/packages/link-modules/src/definitions/index.ts b/packages/link-modules/src/definitions/index.ts index e62cfe17faad2..571e83bcedde3 100644 --- a/packages/link-modules/src/definitions/index.ts +++ b/packages/link-modules/src/definitions/index.ts @@ -2,3 +2,4 @@ export * from "./inventory-level-stock-location" export * from "./product-variant-inventory-item" export * from "./product-variant-price-set" export * from "./product-shipping-profile" +export * from "./product-sales-channel" diff --git a/packages/link-modules/src/definitions/product-sales-channel.ts b/packages/link-modules/src/definitions/product-sales-channel.ts new file mode 100644 index 0000000000000..d3d2ee591b8fc --- /dev/null +++ b/packages/link-modules/src/definitions/product-sales-channel.ts @@ -0,0 +1,64 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" +import { LINKS } from "../links" + +export const ProductSalesChannel: ModuleJoinerConfig = { + serviceName: LINKS.ProductSalesChannel, + isLink: true, + databaseConfig: { + tableName: "product_sales_channel", + idPrefix: "prodsc", + }, + alias: [ + { + name: "product_sales_channel", + }, + { + name: "product_sales_channels", + }, + ], + primaryKeys: ["id", "product_id", "sales_channel_id"], + relationships: [ + { + serviceName: Modules.PRODUCT, + primaryKey: "id", + foreignKey: "product_id", + alias: "product", + // isList: true, + }, + { + serviceName: "salesChannelService", + isInternalService: true, + primaryKey: "id", + foreignKey: "sales_channel_id", + alias: "sales_channel", + // isList: true, + }, + ], + extends: [ + { + serviceName: Modules.PRODUCT, + fieldAlias: { + sales_channels: "sales_channels_link.sales_channel", + }, + relationship: { + serviceName: LINKS.ProductSalesChannel, + primaryKey: "product_id", + foreignKey: "id", + alias: "sales_channels_link", + isList: true, + }, + }, + { + serviceName: "salesChannelService", + relationship: { + serviceName: LINKS.ProductSalesChannel, + isInternalService: true, + primaryKey: "sales_channel_id", + foreignKey: "id", + alias: "products_link", + isList: true, + }, + }, + ], +} diff --git a/packages/link-modules/src/links.ts b/packages/link-modules/src/links.ts index 3c8c3ea942088..bda65e6f2ccba 100644 --- a/packages/link-modules/src/links.ts +++ b/packages/link-modules/src/links.ts @@ -22,4 +22,10 @@ export const LINKS = { "shippingProfileService", "profile_id" ), + ProductSalesChannel: composeLinkName( + Modules.PRODUCT, + "product_id", + "salesChannelService", + "sales_channel_id" + ), } diff --git a/packages/medusa/src/joiner-configs/sales-channel-service.ts b/packages/medusa/src/joiner-configs/sales-channel-service.ts index 401499f1f8080..c67996455eb9c 100644 --- a/packages/medusa/src/joiner-configs/sales-channel-service.ts +++ b/packages/medusa/src/joiner-configs/sales-channel-service.ts @@ -3,7 +3,7 @@ import { ModuleJoinerConfig } from "@medusajs/types" export default { serviceName: "salesChannelService", primaryKeys: ["id"], - linkableKeys: { sales_channel: "SalesChannel" }, + linkableKeys: { sales_channel_id: "SalesChannel" }, schema: ` scalar Date scalar JSON @@ -12,7 +12,7 @@ export default { id: ID! name: String! description: String! - is_disabled: Boolean! + is_disabled: Boolean created_at: Date! updated_at: Date! deleted_at: Date @@ -22,9 +22,11 @@ export default { alias: [ { name: "sales_channel", + args: { entity: "SalesChannel" }, }, { name: "sales_channels", + args: { entity: "SalesChannel" }, }, ], } as ModuleJoinerConfig diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index b9d902ef2881d..e14fc73e3f798 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -158,6 +158,24 @@ class SalesChannelService extends TransactionBaseService { return await salesChannelRepo.findAndCount(query) } + /** + * Lists sales channels based on the provided parameters and includes the count of + * sales channels that match the query. + * @return an array containing the sales channels as + * the first element and the total count of sales channels that matches the query + * as the second element. + */ + async list( + selector: QuerySelector, + config: FindConfig = { + skip: 0, + take: 20, + } + ): Promise { + const [salesChannels] = await this.listAndCount(selector, config) + return salesChannels + } + /** * Creates a SalesChannel * From 268bf9657f00fc498baf83557212d149c455d199 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 23 Oct 2023 12:52:21 +0200 Subject: [PATCH 03/49] feat: migration --- ...98056997411-product-sales-channels-link.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts diff --git a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts new file mode 100644 index 0000000000000..c8b1fc7b762b5 --- /dev/null +++ b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts @@ -0,0 +1,38 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import IsolateProductDomain from "../loaders/feature-flags/isolate-product-domain" + +export const featureFlag = IsolateProductDomain.key + +export class ProductSalesChannelsLink1698056997411 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "product_sales_channel" ADD COLUMN IF NOT EXISTS "id" text; + UPDATE "product_sales_channel" SET "id" = 'prodsc_' || substr(md5(random()::text), 0, 27) WHERE id is NULL; + ALTER TABLE "product_sales_channel" ALTER COLUMN "id" SET NOT NULL; + + ALTER TABLE "product_sales_channel" DROP CONSTRAINT IF EXISTS "PK_fd29b6a8bd641052628dee19583"; + ALTER TABLE "product_sales_channel" ADD CONSTRAINT "product_sales_channel_pk" PRIMARY KEY (id); + ALTER TABLE "product_sales_channel" ADD CONSTRAINT "product_sales_channel_product_id_sales_channel_id_unique" UNIQUE (product_id, sales_channel_id); + + ALTER TABLE "product_sales_channel" ADD COLUMN IF NOT EXISTS "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(); + ALTER TABLE "product_sales_channel" ADD COLUMN IF NOT EXISTS "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(); + ALTER TABLE "product_sales_channel" ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMP WITH TIME ZONE; + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE product_sales_channel DROP CONSTRAINT IF EXISTS "product_sales_channel_pk"; + ALTER TABLE product_sales_channel DROP CONSTRAINT IF EXISTS "product_sales_channel_product_id_sales_channel_id_unique"; + ALTER TABLE product_sales_channel drop column "id"; + + ALTER TABLE "product_sales_channel" DROP COLUMN IF EXISTS "created_at"; + ALTER TABLE "product_sales_channel" DROP COLUMN IF EXISTS "updated_at"; + ALTER TABLE "product_sales_channel" DROP COLUMN IF EXISTS "deleted_at"; + + ALTER TABLE product_sales_channel ADD CONSTRAINT "PK_product_sales_channel" PRIMARY KEY (product_id, sales_channel_id); + `) + } +} From 3898708f69dbaada2429f96efa25e0f88cc65440 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 23 Oct 2023 14:32:32 +0200 Subject: [PATCH 04/49] fix: refactor list SC --- .../medusa/src/repositories/sales-channel.ts | 8 ++-- .../src/services/__tests__/sales-channel.ts | 15 ++++--- packages/medusa/src/services/sales-channel.ts | 39 ++++++++++++++----- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 5c8350d0bdf29..3b7abb9633192 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -8,12 +8,12 @@ const productSalesChannelTable = "product_sales_channel" export const SalesChannelRepository = dataSource .getRepository(SalesChannel) .extend({ - async getFreeTextSearchResultsAndCount( + async getFreeTextSearchResults( q: string, - options: ExtendedFindConfig = { + options: ExtendedFindConfig & { withCount?: boolean } = { where: {}, } - ): Promise<[SalesChannel[], number]> { + ): Promise { const options_ = { ...options } options_.where = options_.where as FindOptionsWhere @@ -41,7 +41,7 @@ export const SalesChannelRepository = dataSource qb = qb.withDeleted() } - return await qb.getManyAndCount() + return await (options_.withCount ? qb.getManyAndCount() : qb.getMany()) }, async removeProducts( diff --git a/packages/medusa/src/services/__tests__/sales-channel.ts b/packages/medusa/src/services/__tests__/sales-channel.ts index 41527ce27d78b..0c8b40413c852 100644 --- a/packages/medusa/src/services/__tests__/sales-channel.ts +++ b/packages/medusa/src/services/__tests__/sales-channel.ts @@ -37,7 +37,7 @@ describe("SalesChannelService", () => { return Promise.resolve() }), }), - getFreeTextSearchResultsAndCount: jest.fn().mockImplementation(() => + getFreeTextSearchResults: jest.fn().mockImplementation(() => Promise.resolve([ { id: IdMap.getId("sales_channel_1"), @@ -139,11 +139,9 @@ describe("SalesChannelService", () => { ...salesChannelData, }) - expect( - salesChannelRepositoryMock.findOne - ).toHaveBeenLastCalledWith({ + expect(salesChannelRepositoryMock.findOne).toHaveBeenLastCalledWith({ where: { id: IdMap.getId("sales_channel_1") }, - relationLoadStrategy: "query" + relationLoadStrategy: "query", }) }) }) @@ -212,14 +210,15 @@ describe("SalesChannelService", () => { expect(salesChannelRepositoryMock.findAndCount).toHaveBeenCalledTimes(0) expect( - salesChannelRepositoryMock.getFreeTextSearchResultsAndCount + salesChannelRepositoryMock.getFreeTextSearchResults ).toHaveBeenCalledTimes(1) expect( - salesChannelRepositoryMock.getFreeTextSearchResultsAndCount + salesChannelRepositoryMock.getFreeTextSearchResults ).toHaveBeenLastCalledWith(q, { skip: 0, take: 20, where: {}, + withCount: true, }) }) @@ -239,7 +238,7 @@ describe("SalesChannelService", () => { ) expect( - salesChannelRepositoryMock.getFreeTextSearchResultsAndCount + salesChannelRepositoryMock.getFreeTextSearchResults ).toHaveBeenCalledTimes(0) expect(salesChannelRepositoryMock.findAndCount).toHaveBeenCalledTimes(1) expect(salesChannelRepositoryMock.findAndCount).toHaveBeenLastCalledWith({ diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index e14fc73e3f798..4c074b164443d 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -125,8 +125,9 @@ class SalesChannelService extends TransactionBaseService { } /** - * Lists sales channels based on the provided parameters and includes the count of + * Lists sales channels based on the provided parameters and include the count of * sales channels that match the query. + * * @return an array containing the sales channels as * the first element and the total count of sales channels that matches the query * as the second element. @@ -152,18 +153,19 @@ class SalesChannelService extends TransactionBaseService { const query = buildQuery(selector_, config) if (q) { - return await salesChannelRepo.getFreeTextSearchResultsAndCount(q, query) + return (await salesChannelRepo.getFreeTextSearchResults(q, { + ...query, + withCount: true, + })) as [SalesChannel[], number] } return await salesChannelRepo.findAndCount(query) } /** - * Lists sales channels based on the provided parameters and includes the count of - * sales channels that match the query. - * @return an array containing the sales channels as - * the first element and the total count of sales channels that matches the query - * as the second element. + * Lists sales channels based on the provided parameters. + * + * @return an array containing the sales channels */ async list( selector: QuerySelector, @@ -172,8 +174,27 @@ class SalesChannelService extends TransactionBaseService { take: 20, } ): Promise { - const [salesChannels] = await this.listAndCount(selector, config) - return salesChannels + const salesChannelRepo = this.activeManager_.withRepository( + this.salesChannelRepository_ + ) + + const selector_ = { ...selector } + let q: string | undefined + if ("q" in selector_) { + q = selector_.q + delete selector_.q + } + + const query = buildQuery(selector_, config) + + if (q) { + return (await salesChannelRepo.getFreeTextSearchResults( + q, + query + )) as SalesChannel[] + } + + return await salesChannelRepo.find(query) } /** From 56c366c9632d7562da54eb98ea74918f49d2bc74 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 23 Oct 2023 16:09:58 +0200 Subject: [PATCH 05/49] refactor: SC repo api --- .../src/definitions/product-sales-channel.ts | 2 -- .../medusa/src/repositories/sales-channel.ts | 26 ++++++++++++++++++- .../src/services/__tests__/sales-channel.ts | 9 +++---- packages/medusa/src/services/sales-channel.ts | 10 ++----- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/link-modules/src/definitions/product-sales-channel.ts b/packages/link-modules/src/definitions/product-sales-channel.ts index d3d2ee591b8fc..8d675284e7e9c 100644 --- a/packages/link-modules/src/definitions/product-sales-channel.ts +++ b/packages/link-modules/src/definitions/product-sales-channel.ts @@ -24,7 +24,6 @@ export const ProductSalesChannel: ModuleJoinerConfig = { primaryKey: "id", foreignKey: "product_id", alias: "product", - // isList: true, }, { serviceName: "salesChannelService", @@ -32,7 +31,6 @@ export const ProductSalesChannel: ModuleJoinerConfig = { primaryKey: "id", foreignKey: "sales_channel_id", alias: "sales_channel", - // isList: true, }, ], extends: [ diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 3b7abb9633192..2be9a2c70e126 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -8,7 +8,7 @@ const productSalesChannelTable = "product_sales_channel" export const SalesChannelRepository = dataSource .getRepository(SalesChannel) .extend({ - async getFreeTextSearchResults( + async getFreeTextSearchResults_( q: string, options: ExtendedFindConfig & { withCount?: boolean } = { where: {}, @@ -44,6 +44,30 @@ export const SalesChannelRepository = dataSource return await (options_.withCount ? qb.getManyAndCount() : qb.getMany()) }, + async getFreeTextSearchResultsAndCount( + q: string, + options: ExtendedFindConfig = { + where: {}, + } + ): Promise<[SalesChannel[], number]> { + return (await this.getFreeTextSearchResults_(q, { + ...options, + withCount: true, + })) as [SalesChannel[], number] + }, + + async getFreeTextSearchResults( + q: string, + options: ExtendedFindConfig = { + where: {}, + } + ): Promise { + return (await this.getFreeTextSearchResults_( + q, + options + )) as SalesChannel[] + }, + async removeProducts( salesChannelId: string, productIds: string[] diff --git a/packages/medusa/src/services/__tests__/sales-channel.ts b/packages/medusa/src/services/__tests__/sales-channel.ts index 0c8b40413c852..41968132100b1 100644 --- a/packages/medusa/src/services/__tests__/sales-channel.ts +++ b/packages/medusa/src/services/__tests__/sales-channel.ts @@ -37,7 +37,7 @@ describe("SalesChannelService", () => { return Promise.resolve() }), }), - getFreeTextSearchResults: jest.fn().mockImplementation(() => + getFreeTextSearchResultsAndCount: jest.fn().mockImplementation(() => Promise.resolve([ { id: IdMap.getId("sales_channel_1"), @@ -210,15 +210,14 @@ describe("SalesChannelService", () => { expect(salesChannelRepositoryMock.findAndCount).toHaveBeenCalledTimes(0) expect( - salesChannelRepositoryMock.getFreeTextSearchResults + salesChannelRepositoryMock.getFreeTextSearchResultsAndCount ).toHaveBeenCalledTimes(1) expect( - salesChannelRepositoryMock.getFreeTextSearchResults + salesChannelRepositoryMock.getFreeTextSearchResultsAndCount ).toHaveBeenLastCalledWith(q, { skip: 0, take: 20, where: {}, - withCount: true, }) }) @@ -238,7 +237,7 @@ describe("SalesChannelService", () => { ) expect( - salesChannelRepositoryMock.getFreeTextSearchResults + salesChannelRepositoryMock.getFreeTextSearchResultsAndCount ).toHaveBeenCalledTimes(0) expect(salesChannelRepositoryMock.findAndCount).toHaveBeenCalledTimes(1) expect(salesChannelRepositoryMock.findAndCount).toHaveBeenLastCalledWith({ diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index 4c074b164443d..7e0116d861594 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -153,10 +153,7 @@ class SalesChannelService extends TransactionBaseService { const query = buildQuery(selector_, config) if (q) { - return (await salesChannelRepo.getFreeTextSearchResults(q, { - ...query, - withCount: true, - })) as [SalesChannel[], number] + return await salesChannelRepo.getFreeTextSearchResultsAndCount(q, query) } return await salesChannelRepo.findAndCount(query) @@ -188,10 +185,7 @@ class SalesChannelService extends TransactionBaseService { const query = buildQuery(selector_, config) if (q) { - return (await salesChannelRepo.getFreeTextSearchResults( - q, - query - )) as SalesChannel[] + return await salesChannelRepo.getFreeTextSearchResults(q, query) } return await salesChannelRepo.find(query) From a475a66dd8648189ded5f62ba19bfab3aed71b40 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 24 Oct 2023 10:34:13 +0200 Subject: [PATCH 06/49] chore: changeset --- .changeset/chilled-deers-prove.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/chilled-deers-prove.md diff --git a/.changeset/chilled-deers-prove.md b/.changeset/chilled-deers-prove.md new file mode 100644 index 0000000000000..7bbed2c322e97 --- /dev/null +++ b/.changeset/chilled-deers-prove.md @@ -0,0 +1,6 @@ +--- +"@medusajs/link-modules": patch +"@medusajs/medusa": patch +--- + +feat(medusa, link-module): SalesChannel<>Product joiner config From a4c410fd06304ed81ec3f66d48ad8b67347e9b27 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 24 Oct 2023 11:09:10 +0200 Subject: [PATCH 07/49] feat: add dedicated FF --- .../feature-flags/isolate-sales-channel-domain.ts | 10 ++++++++++ .../1698056997411-product-sales-channels-link.ts | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts diff --git a/packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts b/packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts new file mode 100644 index 0000000000000..3f6db94f6b2d4 --- /dev/null +++ b/packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts @@ -0,0 +1,10 @@ +import { FlagSettings } from "../../types/feature-flags" + +const IsolateSalesChannelDomainFeatureFlag: FlagSettings = { + key: "isolate_sales_channel_domain", + default_val: false, + env_key: "MEDUSA_FF_ISOLATE_SALES_CHANNEL_DOMAIN", + description: "[WIP] Isolate sales channel domain from the core", +} + +export default IsolateSalesChannelDomainFeatureFlag diff --git a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts index c8b1fc7b762b5..597659ad18708 100644 --- a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts +++ b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts @@ -1,7 +1,7 @@ import { MigrationInterface, QueryRunner } from "typeorm" -import IsolateProductDomain from "../loaders/feature-flags/isolate-product-domain" +import IsolateSalesChannelDomainFeatureFlag from "../loaders/feature-flags/isolate-sales-channel-domain" -export const featureFlag = IsolateProductDomain.key +export const featureFlag = IsolateSalesChannelDomainFeatureFlag.key export class ProductSalesChannelsLink1698056997411 implements MigrationInterface From 0a1ef33127443c4729ac5567d2565c1778a807d0 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 30 Oct 2023 16:27:56 +0100 Subject: [PATCH 08/49] feat: product<> sc join entity --- .../src/models/product-sales-channel.ts | 11 ++++ packages/medusa/src/models/sales-channel.ts | 24 +++++++- .../medusa/src/repositories/sales-channel.ts | 5 +- packages/medusa/src/services/product.ts | 61 +++++++++++++------ .../medusa/src/utils/generate-entity-id.ts | 2 +- 5 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 packages/medusa/src/models/product-sales-channel.ts diff --git a/packages/medusa/src/models/product-sales-channel.ts b/packages/medusa/src/models/product-sales-channel.ts new file mode 100644 index 0000000000000..c3f3dbfe3c87d --- /dev/null +++ b/packages/medusa/src/models/product-sales-channel.ts @@ -0,0 +1,11 @@ +import { Column, Entity } from "typeorm" +import { BaseEntity } from "../interfaces" + +@Entity("product_sales_channel") +export class ProductSalesChannel extends BaseEntity { + @Column({ type: "text" }) + sales_channel_id: string + + @Column({ type: "text" }) + product_id: string +} diff --git a/packages/medusa/src/models/sales-channel.ts b/packages/medusa/src/models/sales-channel.ts index 8c248811881c0..a96103a6afacd 100644 --- a/packages/medusa/src/models/sales-channel.ts +++ b/packages/medusa/src/models/sales-channel.ts @@ -1,9 +1,13 @@ -import { BeforeInsert, Column, OneToMany } from "typeorm" +import { BeforeInsert, Column, JoinTable, ManyToMany, OneToMany } from "typeorm" -import { FeatureFlagEntity } from "../utils/feature-flag-decorators" +import { + FeatureFlagDecorators, + FeatureFlagEntity, +} from "../utils/feature-flag-decorators" import { SoftDeletableEntity } from "../interfaces" import { DbAwareColumn, generateEntityId } from "../utils" import { SalesChannelLocation } from "./sales-channel-location" +import { Product } from "./product" @FeatureFlagEntity("sales_channels") export class SalesChannel extends SoftDeletableEntity { @@ -19,6 +23,22 @@ export class SalesChannel extends SoftDeletableEntity { @DbAwareColumn({ type: "jsonb", nullable: true }) metadata: Record | null + @FeatureFlagDecorators("isolate_sales_channel_domain", [ + ManyToMany(() => Product), + JoinTable({ + name: "product_sales_channel", + inverseJoinColumn: { + name: "product_id", + referencedColumnName: "id", + }, + joinColumn: { + name: "sales_channel_id", + referencedColumnName: "id", + }, + }), + ]) + product: Product[] + @OneToMany( () => SalesChannelLocation, (scLocation) => scLocation.sales_channel, diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 2be9a2c70e126..568a18fe34955 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -2,6 +2,8 @@ import { DeleteResult, FindOptionsWhere, ILike, In } from "typeorm" import { SalesChannel } from "../models" import { ExtendedFindConfig } from "../types/common" import { dataSource } from "../loaders/database" +import { generateEntityId } from "../utils" +import { ProductSalesChannel } from "../models/product-sales-channel" const productSalesChannelTable = "product_sales_channel" @@ -89,13 +91,14 @@ export const SalesChannelRepository = dataSource productIds: string[] ): Promise { const valuesToInsert = productIds.map((id) => ({ + id: generateEntityId(undefined, "prodsc"), sales_channel_id: salesChannelId, product_id: id, })) await this.createQueryBuilder() .insert() - .into(productSalesChannelTable) + .into(ProductSalesChannel) .values(valuesToInsert) .orIgnore() .execute() diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index 5c544d0a3b539..d68c5f70fb951 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -1,6 +1,8 @@ import { - buildRelations, - buildSelects, FlagRouter, objectToStringPath + buildRelations, + buildSelects, + FlagRouter, + objectToStringPath, } from "@medusajs/utils" import { isDefined, MedusaError } from "medusa-core-utils" import { EntityManager, In } from "typeorm" @@ -8,19 +10,19 @@ import { ProductVariantService, SearchService } from "." import { TransactionBaseService } from "../interfaces" import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels" import { - Product, - ProductCategory, - ProductOption, - ProductTag, - ProductType, - ProductVariant, - SalesChannel, - ShippingProfile, + Product, + ProductCategory, + ProductOption, + ProductTag, + ProductType, + ProductVariant, + SalesChannel, + ShippingProfile, } from "../models" import { ImageRepository } from "../repositories/image" import { - FindWithoutRelationsOptions, - ProductRepository, + FindWithoutRelationsOptions, + ProductRepository, } from "../repositories/product" import { ProductCategoryRepository } from "../repositories/product-category" import { ProductOptionRepository } from "../repositories/product-option" @@ -29,15 +31,17 @@ import { ProductTypeRepository } from "../repositories/product-type" import { ProductVariantRepository } from "../repositories/product-variant" import { Selector } from "../types/common" import { - CreateProductInput, - FilterableProductProps, - FindProductConfig, - ProductOptionInput, - ProductSelector, - UpdateProductInput, + CreateProductInput, + FilterableProductProps, + FindProductConfig, + ProductOptionInput, + ProductSelector, + UpdateProductInput, } from "../types/product" import { buildQuery, isString, setMetadata } from "../utils" import EventBusService from "./event-bus" +import SalesChannelService from "./sales-channel" +import IsolateSalesChannelDomain from "../loaders/feature-flags/isolate-sales-channel-domain" type InjectedDependencies = { manager: EntityManager @@ -50,6 +54,7 @@ type InjectedDependencies = { productCategoryRepository: typeof ProductCategoryRepository productVariantService: ProductVariantService searchService: SearchService + salesChannelService: SalesChannelService eventBusService: EventBusService featureFlagRouter: FlagRouter } @@ -65,6 +70,7 @@ class ProductService extends TransactionBaseService { protected readonly productCategoryRepository_: typeof ProductCategoryRepository protected readonly productVariantService_: ProductVariantService protected readonly searchService_: SearchService + protected readonly salesChannelService_: SalesChannelService protected readonly eventBus_: EventBusService protected readonly featureFlagRouter_: FlagRouter @@ -86,6 +92,7 @@ class ProductService extends TransactionBaseService { productCategoryRepository, imageRepository, searchService, + salesChannelService, featureFlagRouter, }: InjectedDependencies) { // eslint-disable-next-line prefer-rest-params @@ -101,6 +108,7 @@ class ProductService extends TransactionBaseService { this.productTagRepository_ = productTagRepository this.imageRepository_ = imageRepository this.searchService_ = searchService + this.salesChannelService_ = salesChannelService this.featureFlagRouter_ = featureFlagRouter } @@ -462,7 +470,8 @@ class ProductService extends TransactionBaseService { } if ( - this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) + this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) && + !this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) ) { if (isDefined(salesChannels)) { product.sales_channels = [] @@ -490,6 +499,20 @@ class ProductService extends TransactionBaseService { product = await productRepo.save(product) + if ( + isDefined(salesChannels) && + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + ) { + if (salesChannels?.length) { + await Promise.all( + salesChannels?.map( + async (sc) => + await this.salesChannelService_.addProducts(sc.id, [product.id]) + ) + ) + } + } + product.options = await Promise.all( (options ?? []).map(async (option) => { const res = optionRepo.create({ diff --git a/packages/medusa/src/utils/generate-entity-id.ts b/packages/medusa/src/utils/generate-entity-id.ts index c85f891ba84c7..4f4f61766e30d 100644 --- a/packages/medusa/src/utils/generate-entity-id.ts +++ b/packages/medusa/src/utils/generate-entity-id.ts @@ -5,7 +5,7 @@ import { ulid } from "ulid" * @param idProperty * @param prefix */ -export function generateEntityId(idProperty: string, prefix?: string): string { +export function generateEntityId(idProperty?: string, prefix?: string): string { if (idProperty) { return idProperty } From 130b5b6986e9d0894764b7d7491903995ef09ea1 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 30 Oct 2023 16:45:24 +0100 Subject: [PATCH 09/49] fix: update case --- packages/medusa/src/services/product.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index d68c5f70fb951..fc96ed5d6fd37 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -637,7 +637,8 @@ class ProductService extends TransactionBaseService { } if ( - this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) + this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) && + !this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) ) { if (isDefined(salesChannels)) { product.sales_channels = [] @@ -660,6 +661,19 @@ class ProductService extends TransactionBaseService { const result = await productRepo.save(product) + if ( + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + ) { + if (salesChannels?.length) { + await Promise.all( + salesChannels?.map( + async (sc) => + await this.salesChannelService_.addProducts(sc.id, [product.id]) + ) + ) + } + } + await this.eventBus_ .withTransaction(manager) .emit(ProductService.Events.UPDATED, { From 6fa9879f9f5aeb15436e5445245bd1cf56b64c07 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 30 Oct 2023 16:57:45 +0100 Subject: [PATCH 10/49] fix: add FF on in the repository, fix tests --- .../medusa/src/repositories/sales-channel.ts | 7 +++++-- .../src/services/__tests__/sales-channel.ts | 12 ++++++++++- packages/medusa/src/services/sales-channel.ts | 20 ++++++++++++++++--- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 568a18fe34955..a69be86e815c0 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -88,10 +88,13 @@ export const SalesChannelRepository = dataSource async addProducts( salesChannelId: string, - productIds: string[] + productIds: string[], + isIsolatedSalesChannelDomainFlagOn?: boolean ): Promise { const valuesToInsert = productIds.map((id) => ({ - id: generateEntityId(undefined, "prodsc"), + id: isIsolatedSalesChannelDomainFlagOn + ? generateEntityId(undefined, "prodsc") + : undefined, sales_channel_id: salesChannelId, product_id: id, })) diff --git a/packages/medusa/src/services/__tests__/sales-channel.ts b/packages/medusa/src/services/__tests__/sales-channel.ts index 41968132100b1..107bb25fc69d0 100644 --- a/packages/medusa/src/services/__tests__/sales-channel.ts +++ b/packages/medusa/src/services/__tests__/sales-channel.ts @@ -3,6 +3,7 @@ import { EventBusService, StoreService } from "../index" import SalesChannelService from "../sales-channel" import { EventBusServiceMock } from "../__mocks__/event-bus" import { store, StoreServiceMock } from "../__mocks__/store" +import { FlagRouter } from "@medusajs/utils" describe("SalesChannelService", () => { const salesChannelData = { @@ -68,6 +69,7 @@ describe("SalesChannelService", () => { eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: StoreServiceMock as unknown as StoreService, + featureFlagRouter: new FlagRouter({}), }) beforeEach(() => { @@ -90,6 +92,7 @@ describe("SalesChannelService", () => { manager: MockManager, eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, + featureFlagRouter: new FlagRouter({}), storeService: { ...StoreServiceMock, retrieve: jest.fn().mockImplementation(() => { @@ -119,6 +122,7 @@ describe("SalesChannelService", () => { describe("retrieve", () => { const salesChannelService = new SalesChannelService({ manager: MockManager, + featureFlagRouter: new FlagRouter({}), eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: StoreServiceMock as unknown as StoreService, @@ -149,6 +153,7 @@ describe("SalesChannelService", () => { describe("update", () => { const salesChannelService = new SalesChannelService({ manager: MockManager, + featureFlagRouter: new FlagRouter({}), eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: StoreServiceMock as unknown as StoreService, @@ -184,6 +189,7 @@ describe("SalesChannelService", () => { describe("list", () => { const salesChannelService = new SalesChannelService({ manager: MockManager, + featureFlagRouter: new FlagRouter({}), eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: StoreServiceMock as unknown as StoreService, @@ -253,6 +259,7 @@ describe("SalesChannelService", () => { describe("delete", () => { const salesChannelService = new SalesChannelService({ manager: MockManager, + featureFlagRouter: new FlagRouter({}), eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: { @@ -308,6 +315,7 @@ describe("SalesChannelService", () => { describe("Remove products", () => { const salesChannelService = new SalesChannelService({ manager: MockManager, + featureFlagRouter: new FlagRouter({}), eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: StoreServiceMock as unknown as StoreService, @@ -339,6 +347,7 @@ describe("SalesChannelService", () => { describe("Add products", () => { const salesChannelService = new SalesChannelService({ manager: MockManager, + featureFlagRouter: new FlagRouter({}), eventBusService: EventBusServiceMock as unknown as EventBusService, salesChannelRepository: salesChannelRepositoryMock, storeService: StoreServiceMock as unknown as StoreService, @@ -357,7 +366,8 @@ describe("SalesChannelService", () => { expect(salesChannelRepositoryMock.addProducts).toHaveBeenCalledTimes(1) expect(salesChannelRepositoryMock.addProducts).toHaveBeenCalledWith( IdMap.getId("sales_channel_1"), - [IdMap.getId("sales_channel_1_product_1")] + [IdMap.getId("sales_channel_1_product_1")], + false ) expect(salesChannel).toBeTruthy() expect(salesChannel).toEqual({ diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index 7e0116d861594..69eda3651ce9a 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -1,23 +1,27 @@ +import { EntityManager } from "typeorm" +import { isDefined, MedusaError } from "medusa-core-utils" +import { FlagRouter } from "@medusajs/utils" + import { FindConfig, QuerySelector, Selector } from "../types/common" import { CreateSalesChannelInput, UpdateSalesChannelInput, } from "../types/sales-channels" -import { isDefined, MedusaError } from "medusa-core-utils" -import { EntityManager } from "typeorm" import { TransactionBaseService } from "../interfaces" import { SalesChannel } from "../models" import { SalesChannelRepository } from "../repositories/sales-channel" import { buildQuery } from "../utils" import EventBusService from "./event-bus" import StoreService from "./store" +import IsolateSalesChannelDomain from "../loaders/feature-flags/isolate-sales-channel-domain" type InjectedDependencies = { salesChannelRepository: typeof SalesChannelRepository eventBusService: EventBusService manager: EntityManager storeService: StoreService + featureFlagRouter: FlagRouter } class SalesChannelService extends TransactionBaseService { @@ -30,11 +34,13 @@ class SalesChannelService extends TransactionBaseService { protected readonly salesChannelRepository_: typeof SalesChannelRepository protected readonly eventBusService_: EventBusService protected readonly storeService_: StoreService + protected readonly featureFlagRouter_: FlagRouter constructor({ salesChannelRepository, eventBusService, storeService, + featureFlagRouter, }: InjectedDependencies) { // eslint-disable-next-line prefer-rest-params super(arguments[0]) @@ -42,6 +48,7 @@ class SalesChannelService extends TransactionBaseService { this.salesChannelRepository_ = salesChannelRepository this.eventBusService_ = eventBusService this.storeService_ = storeService + this.featureFlagRouter_ = featureFlagRouter } /** @@ -387,7 +394,14 @@ class SalesChannelService extends TransactionBaseService { this.salesChannelRepository_ ) - await salesChannelRepo.addProducts(salesChannelId, productIds) + const isIsolatedSalesChannelDomainFlagOn = + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + + await salesChannelRepo.addProducts( + salesChannelId, + productIds, + isIsolatedSalesChannelDomainFlagOn + ) return await this.retrieve(salesChannelId) }) From 67b3b3a315e1b03d360dbfa246bc99b31b659260 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 31 Oct 2023 11:02:08 +0100 Subject: [PATCH 11/49] fix: assign id when FF is on --- packages/medusa/src/repositories/sales-channel.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index a69be86e815c0..95b724ecb3783 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -91,14 +91,18 @@ export const SalesChannelRepository = dataSource productIds: string[], isIsolatedSalesChannelDomainFlagOn?: boolean ): Promise { - const valuesToInsert = productIds.map((id) => ({ - id: isIsolatedSalesChannelDomainFlagOn - ? generateEntityId(undefined, "prodsc") - : undefined, + let valuesToInsert = productIds.map((id) => ({ sales_channel_id: salesChannelId, product_id: id, })) + if (isIsolatedSalesChannelDomainFlagOn) { + valuesToInsert = valuesToInsert.map((v) => ({ + ...v, + id: generateEntityId(undefined, "prodsc"), + })) + } + await this.createQueryBuilder() .insert() .into(ProductSalesChannel) From 5e703796a29102e226a966fcd1f600bb72cacb52 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 31 Oct 2023 11:09:08 +0100 Subject: [PATCH 12/49] fix: target table --- packages/medusa/src/repositories/sales-channel.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 95b724ecb3783..6a89ee0527b15 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -105,7 +105,11 @@ export const SalesChannelRepository = dataSource await this.createQueryBuilder() .insert() - .into(ProductSalesChannel) + .into( + isIsolatedSalesChannelDomainFlagOn + ? ProductSalesChannel + : productSalesChannelTable + ) .values(valuesToInsert) .orIgnore() .execute() From 5bb1b3d88768b68357c75b2c6a3312e76396adb2 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 2 Nov 2023 11:44:42 +0100 Subject: [PATCH 13/49] feat: product service - fetch SC with RQ --- packages/medusa/src/loaders/index.ts | 4 +- packages/medusa/src/services/pricing.ts | 1 - packages/medusa/src/services/product.ts | 109 +++++++++++++++++++++++- 3 files changed, 110 insertions(+), 4 deletions(-) diff --git a/packages/medusa/src/loaders/index.ts b/packages/medusa/src/loaders/index.ts index c5ed4e7184cae..ac3ac51d774e8 100644 --- a/packages/medusa/src/loaders/index.ts +++ b/packages/medusa/src/loaders/index.ts @@ -37,6 +37,7 @@ import servicesLoader from "./services" import strategiesLoader from "./strategies" import subscribersLoader from "./subscribers" import loadMedusaApp from "./medusa-app" +import IsolateSalesChannelDomain from "./feature-flags/isolate-sales-channel-domain" type Options = { directory: string @@ -138,7 +139,8 @@ export default async ({ // Only load non legacy modules, the legacy modules (non migrated yet) are retrieved by the registerModule above if ( featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key) || - featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key) + featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key) || + featureFlagRouter.isFeatureEnabled(IsolateSalesChannelDomain.key) ) { await loadMedusaApp({ configModule, container }) } diff --git a/packages/medusa/src/services/pricing.ts b/packages/medusa/src/services/pricing.ts index 891db6674176d..473146a84cc03 100644 --- a/packages/medusa/src/services/pricing.ts +++ b/packages/medusa/src/services/pricing.ts @@ -2,7 +2,6 @@ import { CalculatedPriceSetDTO, IPricingModuleService, PriceSetMoneyAmountDTO, - RemoteJoinerQuery, RemoteQueryFunction, } from "@medusajs/types" import { FlagRouter, removeNullish } from "@medusajs/utils" diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index 22fe1dccc0d01..e57b8f8dda016 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -4,8 +4,10 @@ import { FlagRouter, objectToStringPath, } from "@medusajs/utils" +import { RemoteQueryFunction } from "@medusajs/types" import { isDefined, MedusaError } from "medusa-core-utils" import { EntityManager, In } from "typeorm" + import { ProductVariantService, SearchService } from "." import { TransactionBaseService } from "../interfaces" import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels" @@ -58,6 +60,7 @@ type InjectedDependencies = { salesChannelService: SalesChannelService eventBusService: EventBusService featureFlagRouter: FlagRouter + remoteQuery: RemoteQueryFunction } class ProductService extends TransactionBaseService { @@ -74,6 +77,7 @@ class ProductService extends TransactionBaseService { protected readonly salesChannelService_: SalesChannelService protected readonly eventBus_: EventBusService protected readonly featureFlagRouter_: FlagRouter + protected remoteQuery_: RemoteQueryFunction static readonly IndexName = `products` static readonly Events = { @@ -93,6 +97,7 @@ class ProductService extends TransactionBaseService { productCategoryRepository, imageRepository, searchService, + remoteQuery, salesChannelService, featureFlagRouter, }: InjectedDependencies) { @@ -111,6 +116,7 @@ class ProductService extends TransactionBaseService { this.searchService_ = searchService this.salesChannelService_ = salesChannelService this.featureFlagRouter_ = featureFlagRouter + this.remoteQuery_ = remoteQuery } /** @@ -172,17 +178,42 @@ class ProductService extends TransactionBaseService { const manager = this.activeManager_ const productRepo = manager.withRepository(this.productRepository_) + const hasSalesChannelsRelation = + config.relations?.includes("sales_channels") + + if ( + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + hasSalesChannelsRelation + ) { + config.relations = config.relations?.filter((r) => r !== "sales_channels") + } + const { q, query, relations } = this.prepareListQuery_(selector, config) + let count: number + let products: Product[] + if (q) { - return await productRepo.getFreeTextSearchResultsAndCount( + ;[products, count] = await productRepo.getFreeTextSearchResultsAndCount( q, query, relations ) + } else { + ;[products, count] = await productRepo.findWithRelationsAndCount( + relations, + query + ) + } + + if ( + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + hasSalesChannelsRelation + ) { + await this.decorateProductsWithSalesChannels(products) } - return await productRepo.findWithRelationsAndCount(relations, query) + return [products, count] } /** @@ -303,6 +334,16 @@ class ProductService extends TransactionBaseService { const manager = this.activeManager_ const productRepo = manager.withRepository(this.productRepository_) + const hasSalesChannelsRelation = + config.relations?.includes("sales_channels") + + if ( + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + hasSalesChannelsRelation + ) { + config.relations = config.relations?.filter((r) => r !== "sales_channels") + } + const { relations, ...query } = buildQuery(selector, config) const product = await productRepo.findOneWithRelations( @@ -321,6 +362,13 @@ class ProductService extends TransactionBaseService { ) } + if ( + this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + hasSalesChannelsRelation + ) { + await this.decorateProductsWithSalesChannels([product]) + } + return product } @@ -1061,6 +1109,63 @@ class ProductService extends TransactionBaseService { q, } } + + /** + * Temporary method to join sales channels of a product using RemoteQuery while + * IsolatedSalesChannelDomain FF is on. + * + * @param products + * @private + */ + private async decorateProductsWithSalesChannels(products: Product[]) { + const productIdSalesChannelMapMap = + await this.getSalesChannelModuleChannels(products.map((p) => p.id)) + + products.forEach( + (product) => + (product.sales_channels = productIdSalesChannelMapMap[product.id] ?? []) + ) + + return products + } + + /** + * Temporary method to fetch sales channels of a product using RemoteQuery while + * IsolatedSalesChannelDomain FF is on. + * + * @param productIds + * @private + */ + private async getSalesChannelModuleChannels( + productIds: string[] + ): Promise> { + const query = { + product: { + __args: { variables: { id: productIds } }, + fields: ["id"], + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + }, + }, + } + + const ret = {} + const data = (await this.remoteQuery_(query)) as { + id: string + sales_channels: SalesChannel[] + }[] + data.forEach((record) => (ret[record.id] = record.sales_channels)) + + return ret + } } export default ProductService From 1e35ae384114e99b6de7aa5c13bee54189ab3527 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 2 Nov 2023 14:19:12 +0100 Subject: [PATCH 14/49] feat: admin list products & SC with isolated product domain --- .../routes/admin/products/list-products.ts | 32 +++++++++++++++++-- packages/medusa/src/services/product.ts | 2 +- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 85542980640cc..e77d0480928ef 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -14,6 +14,7 @@ import { PricedProduct } from "../../../../types/pricing" import { Product } from "../../../../models" import { Type } from "class-transformer" import { defaultAdminProductRemoteQueryObject } from "./index" +import IsolateSalesChannelDomain from "../../../../loaders/feature-flags/isolate-sales-channel-domain" /** * @oas [get] /admin/products @@ -309,6 +310,10 @@ async function listAndCountProductWithIsolatedProductModule( // TODO: Add support for fields/expands const remoteQuery = req.scope.resolve("remoteQuery") + const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomain.key + ) const productIdsFilter: Set = new Set() const variantIdsFilter: Set = new Set() @@ -319,7 +324,7 @@ async function listAndCountProductWithIsolatedProductModule( const salesChannelIdFilter = filterableFields.sales_channel_id delete filterableFields.sales_channel_id - if (salesChannelIdFilter) { + if (salesChannelIdFilter && !isSalesChannelModuleIsolationFFOn) { const salesChannelService = req.scope.resolve( "salesChannelService" ) as SalesChannelService @@ -401,7 +406,26 @@ async function listAndCountProductWithIsolatedProductModule( }, } - const { + if (isSalesChannelModuleIsolationFFOn) { + query.product["sales_channels"] = { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + } + if (salesChannelIdFilter) { + query.product["sales_channels"]["__args"] = { + filters: { id: salesChannelIdFilter }, + } + } + } + + let { rows: products, metadata: { count }, } = await remoteQuery(query) @@ -410,6 +434,10 @@ async function listAndCountProductWithIsolatedProductModule( product.profile_id = product.profile?.id }) + if (salesChannelIdFilter) { + products = products.filter((product) => product.sales_channels?.length) + } + return [products, count] } diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index e57b8f8dda016..f0e5b055a5797 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -1141,7 +1141,7 @@ class ProductService extends TransactionBaseService { ): Promise> { const query = { product: { - __args: { variables: { id: productIds } }, + __args: { filters: { id: productIds } }, fields: ["id"], sales_channels: { fields: [ From 98b34108d655d5f7f4a6ef9831cebfdddf795a43 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 2 Nov 2023 15:23:06 +0100 Subject: [PATCH 15/49] feat: get admin product --- .../api/routes/admin/products/get-product.ts | 50 ++++++++++++++++--- .../routes/admin/products/list-products.ts | 1 + 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/get-product.ts b/packages/medusa/src/api/routes/admin/products/get-product.ts index fc3d3b8135d1c..3670b5d82f19e 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -9,6 +9,7 @@ import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/i import { MedusaError } from "@medusajs/utils" import { FindParams } from "../../../../types/common" import { defaultAdminProductRemoteQueryObject } from "./index" +import IsolateSalesChannelDomain from "../../../../loaders/feature-flags/isolate-sales-channel-domain" /** * @oas [get] /admin/products/{id} @@ -68,12 +69,12 @@ export default async (req, res) => { const productService: ProductService = req.scope.resolve("productService") const pricingService: PricingService = req.scope.resolve("pricingService") const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomain.key + ) const productVariantInventoryService: ProductVariantInventoryService = req.scope.resolve("productVariantInventoryService") - const salesChannelService: SalesChannelService = req.scope.resolve( - "salesChannelService" - ) let rawProduct if (featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key)) { @@ -102,15 +103,30 @@ export default async (req, res) => { req.retrieveConfig.relations?.includes("variants") if (shouldSetAvailability) { - const [salesChannelsIds] = await salesChannelService.listAndCount( - {}, - { select: ["id"] } - ) + let salesChannels + + if (isSalesChannelModuleIsolationFFOn) { + const remoteQuery = req.scope.resolve("remoteQuery") + const query = { + sales_channels: { + fields: ["id", "name"], + }, + } + salesChannels = await remoteQuery(query) + } else { + const salesChannelService: SalesChannelService = req.scope.resolve( + "salesChannelService" + ) + ;[salesChannels] = await salesChannelService.listAndCount( + {}, + { select: ["id"] } + ) + } decoratePromises.push( productVariantInventoryService.setProductAvailability( [product], - salesChannelsIds.map((salesChannel) => salesChannel.id) + salesChannels.map((salesChannel) => salesChannel.id) ) ) } @@ -122,6 +138,10 @@ export default async (req, res) => { async function getProductWithIsolatedProductModule(req, id, retrieveConfig) { // TODO: Add support for fields/expands const remoteQuery = req.scope.resolve("remoteQuery") + const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomain.key + ) const variables = { id } @@ -131,6 +151,20 @@ async function getProductWithIsolatedProductModule(req, id, retrieveConfig) { ...defaultAdminProductRemoteQueryObject, }, } + // TODO: Change when support for fields/expands is added + if (isSalesChannelModuleIsolationFFOn) { + query.product["sales_channels"] = { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + } + } const [product] = await remoteQuery(query) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index e77d0480928ef..7bc1e8092d2a1 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -406,6 +406,7 @@ async function listAndCountProductWithIsolatedProductModule( }, } + // TODO: Change when support for fields/expands is added if (isSalesChannelModuleIsolationFFOn) { query.product["sales_channels"] = { fields: [ From 5c873e2e05ea7c02459d8f7378e0a9c950ed0c31 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 2 Nov 2023 15:51:33 +0100 Subject: [PATCH 16/49] feat: store endpoints --- .../api/routes/store/products/get-product.ts | 21 ++++++++++++ .../routes/store/products/list-products.ts | 33 +++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/medusa/src/api/routes/store/products/get-product.ts b/packages/medusa/src/api/routes/store/products/get-product.ts index 50cef80939117..974f6d4248478 100644 --- a/packages/medusa/src/api/routes/store/products/get-product.ts +++ b/packages/medusa/src/api/routes/store/products/get-product.ts @@ -12,6 +12,7 @@ import IsolateProductDomain from "../../../../loaders/feature-flags/isolate-prod import { PriceSelectionParams } from "../../../../types/price-selection" import { cleanResponseData } from "../../../../utils" import { defaultStoreProductRemoteQueryObject } from "./index" +import IsolateSalesChannelDomain from "../../../../loaders/feature-flags/isolate-sales-channel-domain" /** * @oas [get] /store/products/{id} @@ -165,6 +166,10 @@ export default async (req, res) => { async function getProductWithIsolatedProductModule(req, id: string) { const remoteQuery = req.scope.resolve("remoteQuery") + const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomain.key + ) const variables = { id } @@ -175,6 +180,22 @@ async function getProductWithIsolatedProductModule(req, id: string) { }, } + // TODO: sales_channel filter + + if (isSalesChannelModuleIsolationFFOn) { + query.product["sales_channels"] = { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + } + } + const [product] = await remoteQuery(query) if (!product) { diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index d7f7928044b06..b97063802a628 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -25,6 +25,7 @@ import { cleanResponseData } from "../../../../utils/clean-response-data" import { defaultStoreCategoryScope } from "../product-categories" import { defaultStoreProductRemoteQueryObject } from "./index" import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean" +import IsolateSalesChannelDomain from "../../../../loaders/feature-flags/isolate-sales-channel-domain" /** * @oas [get] /store/products @@ -338,6 +339,10 @@ async function listAndCountProductWithIsolatedProductModule( // TODO: Add support for fields/expands const remoteQuery = req.scope.resolve("remoteQuery") + const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomain.key + ) let salesChannelIdFilter = filterableFields.sales_channel_id if (req.publishableApiKeyScopes?.sales_channel_ids.length) { @@ -360,7 +365,7 @@ async function listAndCountProductWithIsolatedProductModule( } // This is not the best way of handling cross filtering but for now I would say it is fine - if (salesChannelIdFilter) { + if (salesChannelIdFilter && !isSalesChannelModuleIsolationFFOn) { const salesChannelService = req.scope.resolve( "salesChannelService" ) as SalesChannelService @@ -401,7 +406,27 @@ async function listAndCountProductWithIsolatedProductModule( }, } - const { + // TODO: Change when support for fields/expands is added + if (isSalesChannelModuleIsolationFFOn) { + query.product["sales_channels"] = { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + } + if (salesChannelIdFilter) { + query.product["sales_channels"]["__args"] = { + filters: { id: salesChannelIdFilter }, // TODO: check why this isn't working + } + } + } + + let { rows: products, metadata: { count }, } = await remoteQuery(query) @@ -410,6 +435,10 @@ async function listAndCountProductWithIsolatedProductModule( product.profile_id = product.profile?.id }) + if (salesChannelIdFilter) { + products = products.filter((product) => product.sales_channels?.length) + } + return [products, count] } From 1fd544c8cf2e9f181ccaf712db8f936257f86471 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 2 Nov 2023 17:26:16 +0100 Subject: [PATCH 17/49] fix: remove duplicate import --- packages/medusa/src/loaders/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/medusa/src/loaders/index.ts b/packages/medusa/src/loaders/index.ts index f510e07423863..1e47eadd1ece4 100644 --- a/packages/medusa/src/loaders/index.ts +++ b/packages/medusa/src/loaders/index.ts @@ -30,7 +30,6 @@ import searchIndexLoader from "./search-index" import servicesLoader from "./services" import strategiesLoader from "./strategies" import subscribersLoader from "./subscribers" -import loadMedusaApp from "./medusa-app" import IsolateSalesChannelDomain from "./feature-flags/isolate-sales-channel-domain" type Options = { From 8fa7fb50997f4a359ffe375cae5256aba706448f Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 3 Nov 2023 09:31:08 +0100 Subject: [PATCH 18/49] fix: remove "name" prop --- packages/medusa/src/api/routes/admin/products/get-product.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/medusa/src/api/routes/admin/products/get-product.ts b/packages/medusa/src/api/routes/admin/products/get-product.ts index 3670b5d82f19e..a3643000f8c55 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -109,7 +109,7 @@ export default async (req, res) => { const remoteQuery = req.scope.resolve("remoteQuery") const query = { sales_channels: { - fields: ["id", "name"], + fields: ["id"], }, } salesChannels = await remoteQuery(query) From c35b2113e060ff6c4571fc29112753fa2a026db2 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 3 Nov 2023 14:00:20 +0100 Subject: [PATCH 19/49] feat: refactor --- packages/medusa/src/api/routes/admin/products/get-product.ts | 2 +- .../medusa/src/api/routes/store/products/list-products.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/get-product.ts b/packages/medusa/src/api/routes/admin/products/get-product.ts index a3643000f8c55..202be6aa7903e 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -108,7 +108,7 @@ export default async (req, res) => { if (isSalesChannelModuleIsolationFFOn) { const remoteQuery = req.scope.resolve("remoteQuery") const query = { - sales_channels: { + sales_channel: { fields: ["id"], }, } diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index b97063802a628..1cc5c2c469f25 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -420,9 +420,7 @@ async function listAndCountProductWithIsolatedProductModule( ], } if (salesChannelIdFilter) { - query.product["sales_channels"]["__args"] = { - filters: { id: salesChannelIdFilter }, // TODO: check why this isn't working - } + query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } // TODO: check why this isn't working } } From cbb2954cebcff0008a6751d1b1b63cab58a01a6e Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 6 Nov 2023 16:20:34 +0100 Subject: [PATCH 20/49] fix: product seeder if FF is on --- .../factories/simple-product-factory.ts | 25 ++++++++++++++++++- .../utils/src/common/generate-entity-id.ts | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/integration-tests/factories/simple-product-factory.ts b/integration-tests/factories/simple-product-factory.ts index c995d05a46c32..e76582c172830 100644 --- a/integration-tests/factories/simple-product-factory.ts +++ b/integration-tests/factories/simple-product-factory.ts @@ -18,6 +18,7 @@ import { import { DataSource } from "typeorm" import faker from "faker" +import { generateEntityId } from "@medusajs/utils" export type ProductFactoryData = { id?: string @@ -30,6 +31,7 @@ export type ProductFactoryData = { variants?: Omit[] sales_channels?: SalesChannelFactoryData[] metadata?: Record + isIsolatedSalesChannelFFOn?: boolean } export const simpleProductFactory = async ( @@ -41,6 +43,10 @@ export const simpleProductFactory = async ( faker.seed(seed) } + data.isIsolatedSalesChannelFFOn = + data.isIsolatedSalesChannelFFOn ?? + process.env.ISOLATE_SALES_CHANNEL_DOMAIN == "true" + const manager = dataSource.manager const defaultProfile = await manager.findOne(ShippingProfile, { @@ -121,10 +127,27 @@ export const simpleProductFactory = async ( const toSave = manager.create(Product, productToCreate) - toSave.sales_channels = sales_channels + if (!data.isIsolatedSalesChannelFFOn) { + toSave.sales_channels = sales_channels + } const product = await manager.save(toSave) + if (data.isIsolatedSalesChannelFFOn) { + await manager.query( + `INSERT INTO "product_sales_channel" (id, product_id, sales_channel_id) + VALUES ${sales_channels + .map( + (sc) => + `('${generateEntityId(undefined, "prodsc")}', '${toSave.id}', '${ + sc.id + }')` + ) + .join(", ")}; + ` + ) + } + const optionId = `${prodId}-option` const options = data.options || [{ id: optionId, title: "Size" }] for (const o of options) { diff --git a/packages/utils/src/common/generate-entity-id.ts b/packages/utils/src/common/generate-entity-id.ts index c85f891ba84c7..4f4f61766e30d 100644 --- a/packages/utils/src/common/generate-entity-id.ts +++ b/packages/utils/src/common/generate-entity-id.ts @@ -5,7 +5,7 @@ import { ulid } from "ulid" * @param idProperty * @param prefix */ -export function generateEntityId(idProperty: string, prefix?: string): string { +export function generateEntityId(idProperty?: string, prefix?: string): string { if (idProperty) { return idProperty } From 02e81cdc6c7f5d3900c16ed72cdcc89c2fae4f6d Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 6 Nov 2023 18:46:15 +0100 Subject: [PATCH 21/49] fix: env --- integration-tests/factories/simple-product-factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/factories/simple-product-factory.ts b/integration-tests/factories/simple-product-factory.ts index e76582c172830..7f2a66fcac4b0 100644 --- a/integration-tests/factories/simple-product-factory.ts +++ b/integration-tests/factories/simple-product-factory.ts @@ -45,7 +45,7 @@ export const simpleProductFactory = async ( data.isIsolatedSalesChannelFFOn = data.isIsolatedSalesChannelFFOn ?? - process.env.ISOLATE_SALES_CHANNEL_DOMAIN == "true" + process.env.MEDUSA_FF_ISOLATE_SALES_CHANNEL_DOMAIN == "true" const manager = dataSource.manager From 4852a00ffc458dcd2b5264e6b872cc26f0db2087 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 10 Nov 2023 14:43:20 +0100 Subject: [PATCH 22/49] refactor: workflow product handlers to handle remote links --- packages/utils/src/feature-flags/index.ts | 1 + .../isolate-sales-channel-domain.ts | 9 ++++ .../attach-sales-channel-to-products.ts | 54 +++++++++++++++---- .../detach-sales-channel-from-products.ts | 54 +++++++++++++++---- 4 files changed, 98 insertions(+), 20 deletions(-) create mode 100644 packages/utils/src/feature-flags/isolate-sales-channel-domain.ts diff --git a/packages/utils/src/feature-flags/index.ts b/packages/utils/src/feature-flags/index.ts index ffd68c21f61cd..d31765587288a 100644 --- a/packages/utils/src/feature-flags/index.ts +++ b/packages/utils/src/feature-flags/index.ts @@ -4,6 +4,7 @@ export * from "./product-categories" export * from "./publishable-api-keys" export * from "./sales-channels" export * from "./tax-inclusive-pricing" +export * from "./isolate-sales-channel-domain" export * from "./utils" export * from "./workflows" export * from "./many-to-many-inventory" diff --git a/packages/utils/src/feature-flags/isolate-sales-channel-domain.ts b/packages/utils/src/feature-flags/isolate-sales-channel-domain.ts new file mode 100644 index 0000000000000..281d33f8b141e --- /dev/null +++ b/packages/utils/src/feature-flags/isolate-sales-channel-domain.ts @@ -0,0 +1,9 @@ +import { FeatureFlagTypes } from "@medusajs/types" + +export const IsolateSalesChannelDomainFeatureFlag: FeatureFlagTypes.FlagSettings = + { + key: "isolate_sales_channel_domain", + default_val: false, + env_key: "MEDUSA_FF_ISOLATE_SALES_CHANNEL_DOMAIN", + description: "[WIP] Isolate sales channel domain from the core", + } diff --git a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts index 25d970dfd6799..ee8485c6dd2a9 100644 --- a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts +++ b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts @@ -1,5 +1,8 @@ import { WorkflowArguments } from "../../helper" -import { promiseAll } from "@medusajs/utils" +import { + IsolateSalesChannelDomainFeatureFlag, + promiseAll, +} from "@medusajs/utils" type ProductHandle = string type SalesChannelId = string @@ -17,6 +20,8 @@ export async function attachSalesChannelToProducts({ data, }: WorkflowArguments): Promise { const { manager } = context + const featureFlagRouter = container.resolve("featureFlagRouter") + const productsHandleSalesChannelsMap = data.productsHandleSalesChannelsMap const products = data.products @@ -35,16 +40,45 @@ export async function attachSalesChannelToProducts({ } }) - await promiseAll( - Array.from(salesChannelIdProductIdsMap.entries()).map( - async ([salesChannelId, productIds]) => { - return await salesChannelServiceTx.addProducts( - salesChannelId, - productIds - ) - } + if ( + !featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomainFeatureFlag.key + ) + ) { + const remoteLink = container.resolve("remoteLink") + const links: any[] = [] + + for (const [ + salesChannelId, + productIds, + ] of salesChannelIdProductIdsMap.entries()) { + productIds.forEach((id) => + links.push({ + productService: { + product_id: id, + }, + salesChannelService: { + sales_channel_id: salesChannelId, + }, + }) + ) + + await remoteLink.create(links) + } + + return + } else { + await promiseAll( + Array.from(salesChannelIdProductIdsMap.entries()).map( + async ([salesChannelId, productIds]) => { + return await salesChannelServiceTx.addProducts( + salesChannelId, + productIds + ) + } + ) ) - ) + } } attachSalesChannelToProducts.aliases = { diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index 1fafed76b9b41..9c256615601d9 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -1,5 +1,8 @@ import { WorkflowArguments } from "../../helper" -import { promiseAll } from "@medusajs/utils" +import { + IsolateSalesChannelDomainFeatureFlag, + promiseAll, +} from "@medusajs/utils" type ProductHandle = string type SalesChannelId = string @@ -15,6 +18,8 @@ export async function detachSalesChannelFromProducts({ data, }: WorkflowArguments): Promise { const { manager } = context + const featureFlagRouter = container.resolve("featureFlagRouter") + const productsHandleSalesChannelsMap = data.productsHandleSalesChannelsMap const products = data.products @@ -33,16 +38,45 @@ export async function detachSalesChannelFromProducts({ } }) - await promiseAll( - Array.from(salesChannelIdProductIdsMap.entries()).map( - async ([salesChannelId, productIds]) => { - return await salesChannelServiceTx.removeProducts( - salesChannelId, - productIds - ) - } + if ( + !featureFlagRouter.isFeatureEnabled( + IsolateSalesChannelDomainFeatureFlag.key + ) + ) { + const remoteLink = container.resolve("remoteLink") + const links: any[] = [] + + for (const [ + salesChannelId, + productIds, + ] of salesChannelIdProductIdsMap.entries()) { + productIds.forEach((id) => + links.push({ + productService: { + product_id: id, + }, + salesChannelService: { + sales_channel_id: salesChannelId, + }, + }) + ) + + await remoteLink.remove(links) + } + + return + } else { + await promiseAll( + Array.from(salesChannelIdProductIdsMap.entries()).map( + async ([salesChannelId, productIds]) => { + return await salesChannelServiceTx.removeProducts( + salesChannelId, + productIds + ) + } + ) ) - ) + } } detachSalesChannelFromProducts.aliases = { From e219e1032c69e52634538582f51a940b0d224c1e Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 10 Nov 2023 14:59:24 +0100 Subject: [PATCH 23/49] fix: condition --- .../src/handlers/product/attach-sales-channel-to-products.ts | 4 +--- .../handlers/product/detach-sales-channel-from-products.ts | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts index ee8485c6dd2a9..7883fa376fd08 100644 --- a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts +++ b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts @@ -41,9 +41,7 @@ export async function attachSalesChannelToProducts({ }) if ( - !featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomainFeatureFlag.key - ) + featureFlagRouter.isFeatureEnabled(IsolateSalesChannelDomainFeatureFlag.key) ) { const remoteLink = container.resolve("remoteLink") const links: any[] = [] diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index 9c256615601d9..225f5804776d6 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -39,9 +39,7 @@ export async function detachSalesChannelFromProducts({ }) if ( - !featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomainFeatureFlag.key - ) + featureFlagRouter.isFeatureEnabled(IsolateSalesChannelDomainFeatureFlag.key) ) { const remoteLink = container.resolve("remoteLink") const links: any[] = [] From 62c97fcb7f5c36154c58a59bce154e7a9cb95965 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 13 Nov 2023 15:00:26 +0100 Subject: [PATCH 24/49] fix: use correct method --- .../src/handlers/product/detach-sales-channel-from-products.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index 225f5804776d6..b9b413e6819af 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -59,7 +59,7 @@ export async function detachSalesChannelFromProducts({ }) ) - await remoteLink.remove(links) + await remoteLink.delete(links) } return From 916fd009799a9346e9caafa27499371280a3ff55 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 13 Nov 2023 17:01:38 +0100 Subject: [PATCH 25/49] fix: build --- .../medusa/src/api/routes/admin/products/list-products.ts | 8 ++++++-- .../medusa/src/api/routes/store/products/list-products.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 4e68c2645dd62..5bc29b40abbdd 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -8,7 +8,11 @@ import { } from "../../../../services" import { IInventoryService } from "@medusajs/types" -import { MedusaV2Flag, promiseAll } from "@medusajs/utils" +import { + IsolateSalesChannelDomainFeatureFlag, + MedusaV2Flag, + promiseAll, +} from "@medusajs/utils" import { Type } from "class-transformer" import { Product } from "../../../../models" import { PricedProduct } from "../../../../types/pricing" @@ -311,7 +315,7 @@ async function listAndCountProductWithIsolatedProductModule( const remoteQuery = req.scope.resolve("remoteQuery") const featureFlagRouter = req.scope.resolve("featureFlagRouter") const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomain.key + IsolateSalesChannelDomainFeatureFlag.key ) const productIdsFilter: Set = new Set() diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index 47d7970054034..43a7db8294785 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -14,7 +14,11 @@ import { SalesChannelService, } from "../../../../services" -import { MedusaV2Flag, promiseAll } from "@medusajs/utils" +import { + IsolateSalesChannelDomainFeatureFlag, + MedusaV2Flag, + promiseAll, +} from "@medusajs/utils" import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" import PricingService from "../../../../services/pricing" import { DateComparisonOperator } from "../../../../types/common" @@ -340,7 +344,7 @@ async function listAndCountProductWithIsolatedProductModule( const remoteQuery = req.scope.resolve("remoteQuery") const featureFlagRouter = req.scope.resolve("featureFlagRouter") const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomain.key + IsolateSalesChannelDomainFeatureFlag.key ) let salesChannelIdFilter = filterableFields.sales_channel_id From 38ba66fb46280496867cc981e4f5291ceb4d91dc Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 13 Nov 2023 18:07:03 +0100 Subject: [PATCH 26/49] wip: update FF --- .../api/routes/admin/products/get-product.ts | 38 ++++------ .../routes/admin/products/list-products.ts | 72 +++++-------------- .../isolate-sales-channel-domain.ts | 10 --- .../isolate-sales-channel-domain.ts | 9 --- 4 files changed, 31 insertions(+), 98 deletions(-) delete mode 100644 packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts delete mode 100644 packages/utils/src/feature-flags/isolate-sales-channel-domain.ts diff --git a/packages/medusa/src/api/routes/admin/products/get-product.ts b/packages/medusa/src/api/routes/admin/products/get-product.ts index 987acc71f257c..4c28b69f58817 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -8,7 +8,6 @@ import { import { MedusaError, MedusaV2Flag, promiseAll } from "@medusajs/utils" import { FindParams } from "../../../../types/common" import { defaultAdminProductRemoteQueryObject } from "./index" -import IsolateSalesChannelDomain from "../../../../loaders/feature-flags/isolate-sales-channel-domain" /** * @oas [get] /admin/products/{id} @@ -68,15 +67,13 @@ export default async (req, res) => { const productService: ProductService = req.scope.resolve("productService") const pricingService: PricingService = req.scope.resolve("pricingService") const featureFlagRouter = req.scope.resolve("featureFlagRouter") - const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomain.key - ) + const isMedusaV2FlagOn = featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key) const productVariantInventoryService: ProductVariantInventoryService = req.scope.resolve("productVariantInventoryService") let rawProduct - if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) { + if (isMedusaV2FlagOn) { rawProduct = await getProductWithIsolatedProductModule( req, id, @@ -104,7 +101,7 @@ export default async (req, res) => { if (shouldSetAvailability) { let salesChannels - if (isSalesChannelModuleIsolationFFOn) { + if (isMedusaV2FlagOn) { const remoteQuery = req.scope.resolve("remoteQuery") const query = { sales_channel: { @@ -137,10 +134,6 @@ export default async (req, res) => { async function getProductWithIsolatedProductModule(req, id, retrieveConfig) { // TODO: Add support for fields/expands const remoteQuery = req.scope.resolve("remoteQuery") - const featureFlagRouter = req.scope.resolve("featureFlagRouter") - const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomain.key - ) const variables = { id } @@ -148,22 +141,19 @@ async function getProductWithIsolatedProductModule(req, id, retrieveConfig) { product: { __args: variables, ...defaultAdminProductRemoteQueryObject, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + }, }, } - // TODO: Change when support for fields/expands is added - if (isSalesChannelModuleIsolationFFOn) { - query.product["sales_channels"] = { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - } - } const [product] = await remoteQuery(query) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 5bc29b40abbdd..ac9a16654830e 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -8,11 +8,7 @@ import { } from "../../../../services" import { IInventoryService } from "@medusajs/types" -import { - IsolateSalesChannelDomainFeatureFlag, - MedusaV2Flag, - promiseAll, -} from "@medusajs/utils" +import { MedusaV2Flag, promiseAll } from "@medusajs/utils" import { Type } from "class-transformer" import { Product } from "../../../../models" import { PricedProduct } from "../../../../types/pricing" @@ -313,10 +309,6 @@ async function listAndCountProductWithIsolatedProductModule( // TODO: Add support for fields/expands const remoteQuery = req.scope.resolve("remoteQuery") - const featureFlagRouter = req.scope.resolve("featureFlagRouter") - const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomainFeatureFlag.key - ) const productIdsFilter: Set = new Set() const variantIdsFilter: Set = new Set() @@ -327,35 +319,6 @@ async function listAndCountProductWithIsolatedProductModule( const salesChannelIdFilter = filterableFields.sales_channel_id delete filterableFields.sales_channel_id - if (salesChannelIdFilter && !isSalesChannelModuleIsolationFFOn) { - const salesChannelService = req.scope.resolve( - "salesChannelService" - ) as SalesChannelService - - promises.push( - salesChannelService - .listProductIdsBySalesChannelIds(salesChannelIdFilter) - .then((productIdsInSalesChannel) => { - let filteredProductIds = - productIdsInSalesChannel[salesChannelIdFilter] - - if (filterableFields.id) { - filterableFields.id = Array.isArray(filterableFields.id) - ? filterableFields.id - : [filterableFields.id] - - const salesChannelProductIdsSet = new Set(filteredProductIds) - - filteredProductIds = filterableFields.id.filter((productId) => - salesChannelProductIdsSet.has(productId) - ) - } - - filteredProductIds.map((id) => productIdsFilter.add(id)) - }) - ) - } - const priceListId = filterableFields.price_list_id delete filterableFields.price_list_id @@ -402,30 +365,29 @@ async function listAndCountProductWithIsolatedProductModule( take: listConfig.take, } + // TODO: Change when support for fields/expands is added const query = { product: { __args: variables, ...defaultAdminProductRemoteQueryObject, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + }, }, } - // TODO: Change when support for fields/expands is added - if (isSalesChannelModuleIsolationFFOn) { - query.product["sales_channels"] = { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - } - if (salesChannelIdFilter) { - query.product["sales_channels"]["__args"] = { - filters: { id: salesChannelIdFilter }, - } + // TODO: validate that this is filtering correctly + if (salesChannelIdFilter) { + query.product["sales_channels"]["__args"] = { + filters: { id: salesChannelIdFilter }, } } diff --git a/packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts b/packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts deleted file mode 100644 index 3f6db94f6b2d4..0000000000000 --- a/packages/medusa/src/loaders/feature-flags/isolate-sales-channel-domain.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { FlagSettings } from "../../types/feature-flags" - -const IsolateSalesChannelDomainFeatureFlag: FlagSettings = { - key: "isolate_sales_channel_domain", - default_val: false, - env_key: "MEDUSA_FF_ISOLATE_SALES_CHANNEL_DOMAIN", - description: "[WIP] Isolate sales channel domain from the core", -} - -export default IsolateSalesChannelDomainFeatureFlag diff --git a/packages/utils/src/feature-flags/isolate-sales-channel-domain.ts b/packages/utils/src/feature-flags/isolate-sales-channel-domain.ts deleted file mode 100644 index 281d33f8b141e..0000000000000 --- a/packages/utils/src/feature-flags/isolate-sales-channel-domain.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { FeatureFlagTypes } from "@medusajs/types" - -export const IsolateSalesChannelDomainFeatureFlag: FeatureFlagTypes.FlagSettings = - { - key: "isolate_sales_channel_domain", - default_val: false, - env_key: "MEDUSA_FF_ISOLATE_SALES_CHANNEL_DOMAIN", - description: "[WIP] Isolate sales channel domain from the core", - } From 6740977a6502446b77c49ec8a4670a39c506d3c6 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 13 Nov 2023 18:58:10 +0100 Subject: [PATCH 27/49] fix: update FF in the handlers --- .../src/handlers/product/attach-sales-channel-to-products.ts | 5 ++--- .../handlers/product/detach-sales-channel-from-products.ts | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts index 7883fa376fd08..fe11da0cbf323 100644 --- a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts +++ b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts @@ -1,6 +1,7 @@ import { WorkflowArguments } from "../../helper" import { IsolateSalesChannelDomainFeatureFlag, + MedusaV2Flag, promiseAll, } from "@medusajs/utils" @@ -40,9 +41,7 @@ export async function attachSalesChannelToProducts({ } }) - if ( - featureFlagRouter.isFeatureEnabled(IsolateSalesChannelDomainFeatureFlag.key) - ) { + if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) { const remoteLink = container.resolve("remoteLink") const links: any[] = [] diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index b9b413e6819af..01c108a359e4e 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -1,6 +1,7 @@ import { WorkflowArguments } from "../../helper" import { IsolateSalesChannelDomainFeatureFlag, + MedusaV2Flag, promiseAll, } from "@medusajs/utils" @@ -38,9 +39,7 @@ export async function detachSalesChannelFromProducts({ } }) - if ( - featureFlagRouter.isFeatureEnabled(IsolateSalesChannelDomainFeatureFlag.key) - ) { + if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) { const remoteLink = container.resolve("remoteLink") const links: any[] = [] From 976dbff3a9da54dc600c4ed1800bf02dca2fe646 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 10:56:48 +0100 Subject: [PATCH 28/49] chore: migrate to medusav2 FF --- .../api/routes/store/products/get-product.ts | 29 +++++-------- .../routes/store/products/list-products.ts | 41 +++++++------------ ...98056997411-product-sales-channels-link.ts | 4 +- packages/medusa/src/services/product.ts | 20 ++++----- packages/medusa/src/services/sales-channel.ts | 5 +-- packages/utils/src/feature-flags/index.ts | 1 - .../attach-sales-channel-to-products.ts | 6 +-- .../detach-sales-channel-from-products.ts | 6 +-- 8 files changed, 41 insertions(+), 71 deletions(-) diff --git a/packages/medusa/src/api/routes/store/products/get-product.ts b/packages/medusa/src/api/routes/store/products/get-product.ts index 030ccfe6dc968..6c0608a302e38 100644 --- a/packages/medusa/src/api/routes/store/products/get-product.ts +++ b/packages/medusa/src/api/routes/store/products/get-product.ts @@ -11,7 +11,6 @@ import { MedusaError, MedusaV2Flag, promiseAll } from "@medusajs/utils" import { PriceSelectionParams } from "../../../../types/price-selection" import { cleanResponseData } from "../../../../utils" import { defaultStoreProductRemoteQueryObject } from "./index" -import IsolateSalesChannelDomain from "../../../../loaders/feature-flags/isolate-sales-channel-domain" /** * @oas [get] /store/products/{id} @@ -166,9 +165,6 @@ export default async (req, res) => { async function getProductWithIsolatedProductModule(req, id: string) { const remoteQuery = req.scope.resolve("remoteQuery") const featureFlagRouter = req.scope.resolve("featureFlagRouter") - const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomain.key - ) const variables = { id } @@ -176,25 +172,22 @@ async function getProductWithIsolatedProductModule(req, id: string) { product: { __args: variables, ...defaultStoreProductRemoteQueryObject, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + }, }, } // TODO: sales_channel filter - if (isSalesChannelModuleIsolationFFOn) { - query.product["sales_channels"] = { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - } - } - const [product] = await remoteQuery(query) if (!product) { diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index 43a7db8294785..f6e436fad8888 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -14,11 +14,7 @@ import { SalesChannelService, } from "../../../../services" -import { - IsolateSalesChannelDomainFeatureFlag, - MedusaV2Flag, - promiseAll, -} from "@medusajs/utils" +import { MedusaV2Flag, promiseAll } from "@medusajs/utils" import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" import PricingService from "../../../../services/pricing" import { DateComparisonOperator } from "../../../../types/common" @@ -342,10 +338,6 @@ async function listAndCountProductWithIsolatedProductModule( // TODO: Add support for fields/expands const remoteQuery = req.scope.resolve("remoteQuery") - const featureFlagRouter = req.scope.resolve("featureFlagRouter") - const isSalesChannelModuleIsolationFFOn = featureFlagRouter.isFeatureEnabled( - IsolateSalesChannelDomainFeatureFlag.key - ) let salesChannelIdFilter = filterableFields.sales_channel_id if (req.publishableApiKeyScopes?.sales_channel_ids.length) { @@ -368,7 +360,7 @@ async function listAndCountProductWithIsolatedProductModule( } // This is not the best way of handling cross filtering but for now I would say it is fine - if (salesChannelIdFilter && !isSalesChannelModuleIsolationFFOn) { + if (salesChannelIdFilter) { const salesChannelService = req.scope.resolve( "salesChannelService" ) as SalesChannelService @@ -406,25 +398,22 @@ async function listAndCountProductWithIsolatedProductModule( product: { __args: variables, ...defaultStoreProductRemoteQueryObject, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + }, }, } - // TODO: Change when support for fields/expands is added - if (isSalesChannelModuleIsolationFFOn) { - query.product["sales_channels"] = { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - } - if (salesChannelIdFilter) { - query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } // TODO: check why this isn't working - } + if (salesChannelIdFilter) { + query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } // TODO: check this } let { diff --git a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts index 597659ad18708..c0f96b629d025 100644 --- a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts +++ b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts @@ -1,7 +1,7 @@ import { MigrationInterface, QueryRunner } from "typeorm" -import IsolateSalesChannelDomainFeatureFlag from "../loaders/feature-flags/isolate-sales-channel-domain" +import { MedusaV2Flag } from "@medusajs/utils" -export const featureFlag = IsolateSalesChannelDomainFeatureFlag.key +export const featureFlag = MedusaV2Flag.key export class ProductSalesChannelsLink1698056997411 implements MigrationInterface diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index d91d83c1b6c8d..ef924523c86a6 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -2,6 +2,7 @@ import { buildRelations, buildSelects, FlagRouter, + MedusaV2Flag, objectToStringPath, promiseAll, } from "@medusajs/utils" @@ -45,7 +46,6 @@ import { buildQuery, isString, setMetadata } from "../utils" import EventBusService from "./event-bus" import { CreateProductVariantInput } from "../types/product-variant" import SalesChannelService from "./sales-channel" -import IsolateSalesChannelDomain from "../loaders/feature-flags/isolate-sales-channel-domain" type InjectedDependencies = { manager: EntityManager @@ -183,7 +183,7 @@ class ProductService extends TransactionBaseService { config.relations?.includes("sales_channels") if ( - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) && hasSalesChannelsRelation ) { config.relations = config.relations?.filter((r) => r !== "sales_channels") @@ -208,7 +208,7 @@ class ProductService extends TransactionBaseService { } if ( - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) && hasSalesChannelsRelation ) { await this.decorateProductsWithSalesChannels(products) @@ -339,7 +339,7 @@ class ProductService extends TransactionBaseService { config.relations?.includes("sales_channels") if ( - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) && hasSalesChannelsRelation ) { config.relations = config.relations?.filter((r) => r !== "sales_channels") @@ -364,7 +364,7 @@ class ProductService extends TransactionBaseService { } if ( - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) && + this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) && hasSalesChannelsRelation ) { await this.decorateProductsWithSalesChannels([product]) @@ -522,7 +522,7 @@ class ProductService extends TransactionBaseService { if ( this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) && - !this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + !this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) ) { if (isDefined(salesChannels)) { product.sales_channels = [] @@ -552,7 +552,7 @@ class ProductService extends TransactionBaseService { if ( isDefined(salesChannels) && - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) ) { if (salesChannels?.length) { await Promise.all( @@ -710,7 +710,7 @@ class ProductService extends TransactionBaseService { if ( this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) && - !this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + !this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) ) { if (isDefined(salesChannels)) { product.sales_channels = [] @@ -733,9 +733,7 @@ class ProductService extends TransactionBaseService { const result = await productRepo.save(product) - if ( - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) - ) { + if (this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key)) { if (salesChannels?.length) { await promiseAll( salesChannels?.map( diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index 69eda3651ce9a..900401d77d569 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -1,6 +1,6 @@ import { EntityManager } from "typeorm" import { isDefined, MedusaError } from "medusa-core-utils" -import { FlagRouter } from "@medusajs/utils" +import { FlagRouter, MedusaV2Flag } from "@medusajs/utils" import { FindConfig, QuerySelector, Selector } from "../types/common" import { @@ -14,7 +14,6 @@ import { SalesChannelRepository } from "../repositories/sales-channel" import { buildQuery } from "../utils" import EventBusService from "./event-bus" import StoreService from "./store" -import IsolateSalesChannelDomain from "../loaders/feature-flags/isolate-sales-channel-domain" type InjectedDependencies = { salesChannelRepository: typeof SalesChannelRepository @@ -395,7 +394,7 @@ class SalesChannelService extends TransactionBaseService { ) const isIsolatedSalesChannelDomainFlagOn = - this.featureFlagRouter_.isFeatureEnabled(IsolateSalesChannelDomain.key) + this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) await salesChannelRepo.addProducts( salesChannelId, diff --git a/packages/utils/src/feature-flags/index.ts b/packages/utils/src/feature-flags/index.ts index ba72a0849f2ca..1842f1ececdc9 100644 --- a/packages/utils/src/feature-flags/index.ts +++ b/packages/utils/src/feature-flags/index.ts @@ -6,6 +6,5 @@ export * from "./product-categories" export * from "./publishable-api-keys" export * from "./sales-channels" export * from "./tax-inclusive-pricing" -export * from "./isolate-sales-channel-domain" export * from "./utils" export * from "./workflows" diff --git a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts index fe11da0cbf323..9d939d63e4278 100644 --- a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts +++ b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts @@ -1,9 +1,5 @@ import { WorkflowArguments } from "../../helper" -import { - IsolateSalesChannelDomainFeatureFlag, - MedusaV2Flag, - promiseAll, -} from "@medusajs/utils" +import { MedusaV2Flag, promiseAll } from "@medusajs/utils" type ProductHandle = string type SalesChannelId = string diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index 01c108a359e4e..85709fe360004 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -1,9 +1,5 @@ import { WorkflowArguments } from "../../helper" -import { - IsolateSalesChannelDomainFeatureFlag, - MedusaV2Flag, - promiseAll, -} from "@medusajs/utils" +import { MedusaV2Flag, promiseAll } from "@medusajs/utils" type ProductHandle = string type SalesChannelId = string From 5f3f23ebd4a8ef81dea2e221f4f3f14d1a16bfbc Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 11:03:42 +0100 Subject: [PATCH 29/49] chore: uncomment test --- .../plugins/__tests__/product/admin/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/integration-tests/plugins/__tests__/product/admin/index.ts b/integration-tests/plugins/__tests__/product/admin/index.ts index c46ace8b44d0f..006cfea4ed4a8 100644 --- a/integration-tests/plugins/__tests__/product/admin/index.ts +++ b/integration-tests/plugins/__tests__/product/admin/index.ts @@ -583,11 +583,10 @@ describe("/admin/products", () => { expect(response?.data.product).toEqual( expect.objectContaining({ id: toUpdateWithSalesChannels, - // TODO: Introduce this in the sale channel PR - // sales_channels: [ - // expect.objectContaining({ id: "channel-2" }), - // expect.objectContaining({ id: "channel-3" }), - // ], + sales_channels: [ + expect.objectContaining({ id: "channel-2" }), + expect.objectContaining({ id: "channel-3" }), + ], }) ) }) From b2a808acda88c537af1fc29d77cb3613f5c393e9 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 11:18:00 +0100 Subject: [PATCH 30/49] fix: product factory --- integration-tests/factories/simple-product-factory.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/integration-tests/factories/simple-product-factory.ts b/integration-tests/factories/simple-product-factory.ts index 7f2a66fcac4b0..dbb5e8f9b66f2 100644 --- a/integration-tests/factories/simple-product-factory.ts +++ b/integration-tests/factories/simple-product-factory.ts @@ -44,8 +44,7 @@ export const simpleProductFactory = async ( } data.isIsolatedSalesChannelFFOn = - data.isIsolatedSalesChannelFFOn ?? - process.env.MEDUSA_FF_ISOLATE_SALES_CHANNEL_DOMAIN == "true" + data.isIsolatedSalesChannelFFOn ?? process.env.MEDUSA_FF_MEDUSA_V2 == "true" const manager = dataSource.manager From 759d33131920cc396c8d36283bab9b37dd672094 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 13:24:32 +0100 Subject: [PATCH 31/49] fix: unlinking SC and product --- .../plugins/__tests__/product/admin/index.ts | 2 +- .../routes/admin/products/update-product.ts | 11 ++++++++++ packages/medusa/src/services/product.ts | 4 ++-- .../detach-sales-channel-from-products.ts | 22 +++++++++---------- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/integration-tests/plugins/__tests__/product/admin/index.ts b/integration-tests/plugins/__tests__/product/admin/index.ts index 006cfea4ed4a8..d47bc71493341 100644 --- a/integration-tests/plugins/__tests__/product/admin/index.ts +++ b/integration-tests/plugins/__tests__/product/admin/index.ts @@ -571,7 +571,7 @@ describe("/admin/products", () => { const response = await api .post( - `/admin/products/${toUpdateWithSalesChannels}`, + `/admin/products/${toUpdateWithSalesChannels}?expand=sales_channels`, payload, adminHeaders ) diff --git a/packages/medusa/src/api/routes/admin/products/update-product.ts b/packages/medusa/src/api/routes/admin/products/update-product.ts index e559fbf49edb9..a4d3a647f89f7 100644 --- a/packages/medusa/src/api/routes/admin/products/update-product.ts +++ b/packages/medusa/src/api/routes/admin/products/update-product.ts @@ -315,6 +315,17 @@ async function getProductWithIsolatedProductModule(req, id) { product: { __args: variables, ...defaultAdminProductRemoteQueryObject, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + ], + }, }, } diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index ef924523c86a6..f282c2d67566c 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -1111,7 +1111,7 @@ class ProductService extends TransactionBaseService { /** * Temporary method to join sales channels of a product using RemoteQuery while - * IsolatedSalesChannelDomain FF is on. + * MedusaV2 FF is on. * * @param products * @private @@ -1130,7 +1130,7 @@ class ProductService extends TransactionBaseService { /** * Temporary method to fetch sales channels of a product using RemoteQuery while - * IsolatedSalesChannelDomain FF is on. + * MedusaV2 FF is on. * * @param productIds * @private diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index 85709fe360004..efc9623e48ae9 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -37,24 +37,24 @@ export async function detachSalesChannelFromProducts({ if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) { const remoteLink = container.resolve("remoteLink") - const links: any[] = [] + const promises: Promise[] = [] for (const [ salesChannelId, productIds, ] of salesChannelIdProductIdsMap.entries()) { productIds.forEach((id) => - links.push({ - productService: { - product_id: id, - }, - salesChannelService: { - sales_channel_id: salesChannelId, - }, - }) + promises.push( + remoteLink.dismiss({ + productService: { + product_id: id, + }, + salesChannelService: { + sales_channel_id: salesChannelId, + }, + }) + ) ) - - await remoteLink.delete(links) } return From 3634fbac2e48d33ea8fda100dc10ed7a666c603e Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 13:36:36 +0100 Subject: [PATCH 32/49] fix: use module name variable --- .../src/handlers/product/attach-sales-channel-to-products.ts | 3 ++- .../src/handlers/product/detach-sales-channel-from-products.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts index 9d939d63e4278..de7008d1ca5f5 100644 --- a/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts +++ b/packages/workflows/src/handlers/product/attach-sales-channel-to-products.ts @@ -1,5 +1,6 @@ import { WorkflowArguments } from "../../helper" import { MedusaV2Flag, promiseAll } from "@medusajs/utils" +import { Modules } from "@medusajs/modules-sdk" type ProductHandle = string type SalesChannelId = string @@ -47,7 +48,7 @@ export async function attachSalesChannelToProducts({ ] of salesChannelIdProductIdsMap.entries()) { productIds.forEach((id) => links.push({ - productService: { + [Modules.PRODUCT]: { product_id: id, }, salesChannelService: { diff --git a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts index efc9623e48ae9..d71c4548a4929 100644 --- a/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/workflows/src/handlers/product/detach-sales-channel-from-products.ts @@ -1,5 +1,6 @@ import { WorkflowArguments } from "../../helper" import { MedusaV2Flag, promiseAll } from "@medusajs/utils" +import { Modules } from "@medusajs/modules-sdk" type ProductHandle = string type SalesChannelId = string @@ -46,7 +47,7 @@ export async function detachSalesChannelFromProducts({ productIds.forEach((id) => promises.push( remoteLink.dismiss({ - productService: { + [Modules.PRODUCT]: { product_id: id, }, salesChannelService: { From e6eb846807c510c905ef797513f37a8b05733152 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 13:49:08 +0100 Subject: [PATCH 33/49] refactor: cleanup query definitions --- .../src/api/routes/admin/products/get-product.ts | 11 ----------- .../medusa/src/api/routes/admin/products/index.ts | 13 ++++++++++++- .../src/api/routes/admin/products/list-products.ts | 11 ----------- .../src/api/routes/admin/products/update-product.ts | 11 ----------- .../src/api/routes/store/products/get-product.ts | 12 ------------ .../medusa/src/api/routes/store/products/index.ts | 12 ++++++++++++ .../src/api/routes/store/products/list-products.ts | 11 ----------- packages/medusa/src/models/sales-channel.ts | 3 ++- 8 files changed, 26 insertions(+), 58 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/get-product.ts b/packages/medusa/src/api/routes/admin/products/get-product.ts index 4c28b69f58817..ee86ee6d900e9 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -141,17 +141,6 @@ async function getProductWithIsolatedProductModule(req, id, retrieveConfig) { product: { __args: variables, ...defaultAdminProductRemoteQueryObject, - sales_channels: { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - }, }, } diff --git a/packages/medusa/src/api/routes/admin/products/index.ts b/packages/medusa/src/api/routes/admin/products/index.ts index 68391429b66e2..6d6c092a5af35 100644 --- a/packages/medusa/src/api/routes/admin/products/index.ts +++ b/packages/medusa/src/api/routes/admin/products/index.ts @@ -226,6 +226,18 @@ export const defaultAdminProductRemoteQueryObject = { profile: { fields: ["id", "created_at", "updated_at", "deleted_at", "name", "type"], }, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + "metadata", + ], + }, } /** @@ -503,4 +515,3 @@ export * from "./set-metadata" export * from "./update-option" export * from "./update-product" export * from "./update-variant" - diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index ac9a16654830e..6f382c257c4c1 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -370,17 +370,6 @@ async function listAndCountProductWithIsolatedProductModule( product: { __args: variables, ...defaultAdminProductRemoteQueryObject, - sales_channels: { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - }, }, } diff --git a/packages/medusa/src/api/routes/admin/products/update-product.ts b/packages/medusa/src/api/routes/admin/products/update-product.ts index a4d3a647f89f7..e559fbf49edb9 100644 --- a/packages/medusa/src/api/routes/admin/products/update-product.ts +++ b/packages/medusa/src/api/routes/admin/products/update-product.ts @@ -315,17 +315,6 @@ async function getProductWithIsolatedProductModule(req, id) { product: { __args: variables, ...defaultAdminProductRemoteQueryObject, - sales_channels: { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - }, }, } diff --git a/packages/medusa/src/api/routes/store/products/get-product.ts b/packages/medusa/src/api/routes/store/products/get-product.ts index 6c0608a302e38..c3edceed6fe01 100644 --- a/packages/medusa/src/api/routes/store/products/get-product.ts +++ b/packages/medusa/src/api/routes/store/products/get-product.ts @@ -164,7 +164,6 @@ export default async (req, res) => { async function getProductWithIsolatedProductModule(req, id: string) { const remoteQuery = req.scope.resolve("remoteQuery") - const featureFlagRouter = req.scope.resolve("featureFlagRouter") const variables = { id } @@ -172,17 +171,6 @@ async function getProductWithIsolatedProductModule(req, id: string) { product: { __args: variables, ...defaultStoreProductRemoteQueryObject, - sales_channels: { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - }, }, } diff --git a/packages/medusa/src/api/routes/store/products/index.ts b/packages/medusa/src/api/routes/store/products/index.ts index ae07e8f90805e..eded227c34f8c 100644 --- a/packages/medusa/src/api/routes/store/products/index.ts +++ b/packages/medusa/src/api/routes/store/products/index.ts @@ -198,6 +198,18 @@ export const defaultStoreProductRemoteQueryObject = { profile: { fields: ["id", "created_at", "updated_at", "deleted_at", "name", "type"], }, + sales_channels: { + fields: [ + "id", + "name", + "description", + "is_disabled", + "created_at", + "updated_at", + "deleted_at", + "metadata", + ], + }, } export * from "./list-products" diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index f6e436fad8888..5c9f84bc1442a 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -398,17 +398,6 @@ async function listAndCountProductWithIsolatedProductModule( product: { __args: variables, ...defaultStoreProductRemoteQueryObject, - sales_channels: { - fields: [ - "id", - "name", - "description", - "is_disabled", - "created_at", - "updated_at", - "deleted_at", - ], - }, }, } diff --git a/packages/medusa/src/models/sales-channel.ts b/packages/medusa/src/models/sales-channel.ts index 8bd6f55953443..c407d27e5fba3 100644 --- a/packages/medusa/src/models/sales-channel.ts +++ b/packages/medusa/src/models/sales-channel.ts @@ -1,4 +1,5 @@ import { BeforeInsert, Column, JoinTable, ManyToMany, OneToMany } from "typeorm" +import { MedusaV2Flag } from "@medusajs/utils" import { FeatureFlagDecorators, @@ -23,7 +24,7 @@ export class SalesChannel extends SoftDeletableEntity { @DbAwareColumn({ type: "jsonb", nullable: true }) metadata: Record | null - @FeatureFlagDecorators("isolate_sales_channel_domain", [ + @FeatureFlagDecorators(MedusaV2Flag.key, [ ManyToMany(() => Product), JoinTable({ name: "product_sales_channel", From 02adccfe1c2edf909a0cb7c52643b43fdffbb36b Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 14 Nov 2023 15:23:56 +0100 Subject: [PATCH 34/49] fix: add constraint --- .../src/migrations/1698056997411-product-sales-channels-link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts index c0f96b629d025..762c9db6dcb93 100644 --- a/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts +++ b/packages/medusa/src/migrations/1698056997411-product-sales-channels-link.ts @@ -26,7 +26,7 @@ export class ProductSalesChannelsLink1698056997411 await queryRunner.query(` ALTER TABLE product_sales_channel DROP CONSTRAINT IF EXISTS "product_sales_channel_pk"; ALTER TABLE product_sales_channel DROP CONSTRAINT IF EXISTS "product_sales_channel_product_id_sales_channel_id_unique"; - ALTER TABLE product_sales_channel drop column "id"; + ALTER TABLE product_sales_channel drop column if exists "id"; ALTER TABLE "product_sales_channel" DROP COLUMN IF EXISTS "created_at"; ALTER TABLE "product_sales_channel" DROP COLUMN IF EXISTS "updated_at"; From 984060802c8858c20a3a5ae32beb53859507e922 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 15 Nov 2023 16:13:54 +0100 Subject: [PATCH 35/49] chore: rename prop --- packages/medusa/src/models/sales-channel.ts | 34 +++++++++------------ 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/medusa/src/models/sales-channel.ts b/packages/medusa/src/models/sales-channel.ts index c407d27e5fba3..a25bc2e7f8f05 100644 --- a/packages/medusa/src/models/sales-channel.ts +++ b/packages/medusa/src/models/sales-channel.ts @@ -1,10 +1,6 @@ import { BeforeInsert, Column, JoinTable, ManyToMany, OneToMany } from "typeorm" -import { MedusaV2Flag } from "@medusajs/utils" -import { - FeatureFlagDecorators, - FeatureFlagEntity, -} from "../utils/feature-flag-decorators" +import { FeatureFlagEntity } from "../utils/feature-flag-decorators" import { SoftDeletableEntity } from "../interfaces" import { DbAwareColumn, generateEntityId } from "../utils" import { SalesChannelLocation } from "./sales-channel-location" @@ -24,21 +20,19 @@ export class SalesChannel extends SoftDeletableEntity { @DbAwareColumn({ type: "jsonb", nullable: true }) metadata: Record | null - @FeatureFlagDecorators(MedusaV2Flag.key, [ - ManyToMany(() => Product), - JoinTable({ - name: "product_sales_channel", - inverseJoinColumn: { - name: "product_id", - referencedColumnName: "id", - }, - joinColumn: { - name: "sales_channel_id", - referencedColumnName: "id", - }, - }), - ]) - product: Product[] + @ManyToMany(() => Product) + @JoinTable({ + name: "product_sales_channel", + inverseJoinColumn: { + name: "product_id", + referencedColumnName: "id", + }, + joinColumn: { + name: "sales_channel_id", + referencedColumnName: "id", + }, + }) + products: Product[] @OneToMany( () => SalesChannelLocation, From 2457d8886bc51841093856ab15b7d239f8f9312a Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 15 Nov 2023 16:14:55 +0100 Subject: [PATCH 36/49] fix: add hook --- packages/medusa/src/models/product-sales-channel.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/models/product-sales-channel.ts b/packages/medusa/src/models/product-sales-channel.ts index c3f3dbfe3c87d..75792c3cb2d08 100644 --- a/packages/medusa/src/models/product-sales-channel.ts +++ b/packages/medusa/src/models/product-sales-channel.ts @@ -1,5 +1,6 @@ -import { Column, Entity } from "typeorm" +import { BeforeInsert, Column, Entity } from "typeorm" import { BaseEntity } from "../interfaces" +import { generateEntityId } from "../utils" @Entity("product_sales_channel") export class ProductSalesChannel extends BaseEntity { @@ -8,4 +9,12 @@ export class ProductSalesChannel extends BaseEntity { @Column({ type: "text" }) product_id: string + + /** + * @apiIgnore + */ + @BeforeInsert() + private beforeInsert(): void { + this.id = generateEntityId(this.id, "prodsc") + } } From 54a7367e77131324f82970c7687276f00b8c73c2 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 16 Nov 2023 10:56:20 +0100 Subject: [PATCH 37/49] fix: address comments --- integration-tests/factories/simple-product-factory.ts | 10 +++++----- .../src/api/routes/admin/products/list-products.ts | 1 - .../src/api/routes/store/products/get-product.ts | 2 -- .../src/api/routes/store/products/list-products.ts | 2 +- packages/medusa/src/repositories/sales-channel.ts | 8 +++----- packages/medusa/src/services/sales-channel.ts | 7 ++++--- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/integration-tests/factories/simple-product-factory.ts b/integration-tests/factories/simple-product-factory.ts index dbb5e8f9b66f2..2dbbc5265a25c 100644 --- a/integration-tests/factories/simple-product-factory.ts +++ b/integration-tests/factories/simple-product-factory.ts @@ -31,7 +31,7 @@ export type ProductFactoryData = { variants?: Omit[] sales_channels?: SalesChannelFactoryData[] metadata?: Record - isIsolatedSalesChannelFFOn?: boolean + isMedusaV2Enabled?: boolean } export const simpleProductFactory = async ( @@ -43,8 +43,8 @@ export const simpleProductFactory = async ( faker.seed(seed) } - data.isIsolatedSalesChannelFFOn = - data.isIsolatedSalesChannelFFOn ?? process.env.MEDUSA_FF_MEDUSA_V2 == "true" + data.isMedusaV2Enabled = + data.isMedusaV2Enabled ?? process.env.MEDUSA_FF_MEDUSA_V2 == "true" const manager = dataSource.manager @@ -126,13 +126,13 @@ export const simpleProductFactory = async ( const toSave = manager.create(Product, productToCreate) - if (!data.isIsolatedSalesChannelFFOn) { + if (!data.isMedusaV2Enabled) { toSave.sales_channels = sales_channels } const product = await manager.save(toSave) - if (data.isIsolatedSalesChannelFFOn) { + if (data.isMedusaV2Enabled) { await manager.query( `INSERT INTO "product_sales_channel" (id, product_id, sales_channel_id) VALUES ${sales_channels diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 6f382c257c4c1..0342451112618 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -373,7 +373,6 @@ async function listAndCountProductWithIsolatedProductModule( }, } - // TODO: validate that this is filtering correctly if (salesChannelIdFilter) { query.product["sales_channels"]["__args"] = { filters: { id: salesChannelIdFilter }, diff --git a/packages/medusa/src/api/routes/store/products/get-product.ts b/packages/medusa/src/api/routes/store/products/get-product.ts index c3edceed6fe01..22bd7ca0a4e52 100644 --- a/packages/medusa/src/api/routes/store/products/get-product.ts +++ b/packages/medusa/src/api/routes/store/products/get-product.ts @@ -174,8 +174,6 @@ async function getProductWithIsolatedProductModule(req, id: string) { }, } - // TODO: sales_channel filter - const [product] = await remoteQuery(query) if (!product) { diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index 5c9f84bc1442a..e01d3d013f42b 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -402,7 +402,7 @@ async function listAndCountProductWithIsolatedProductModule( } if (salesChannelIdFilter) { - query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } // TODO: check this + query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } } let { diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 6a89ee0527b15..e4d35442fccbe 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -89,14 +89,14 @@ export const SalesChannelRepository = dataSource async addProducts( salesChannelId: string, productIds: string[], - isIsolatedSalesChannelDomainFlagOn?: boolean + isMedusaV2Enabled?: boolean ): Promise { let valuesToInsert = productIds.map((id) => ({ sales_channel_id: salesChannelId, product_id: id, })) - if (isIsolatedSalesChannelDomainFlagOn) { + if (isMedusaV2Enabled) { valuesToInsert = valuesToInsert.map((v) => ({ ...v, id: generateEntityId(undefined, "prodsc"), @@ -106,9 +106,7 @@ export const SalesChannelRepository = dataSource await this.createQueryBuilder() .insert() .into( - isIsolatedSalesChannelDomainFlagOn - ? ProductSalesChannel - : productSalesChannelTable + isMedusaV2Enabled ? ProductSalesChannel : productSalesChannelTable ) .values(valuesToInsert) .orIgnore() diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index 900401d77d569..51f734bcda0f7 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -393,13 +393,14 @@ class SalesChannelService extends TransactionBaseService { this.salesChannelRepository_ ) - const isIsolatedSalesChannelDomainFlagOn = - this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) + const isMedusaV2Enabled = this.featureFlagRouter_.isFeatureEnabled( + MedusaV2Flag.key + ) await salesChannelRepo.addProducts( salesChannelId, productIds, - isIsolatedSalesChannelDomainFlagOn + isMedusaV2Enabled ) return await this.retrieve(salesChannelId) From db4f52c3b0338879b319a9566a19ddd585451abc Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 16 Nov 2023 12:18:51 +0100 Subject: [PATCH 38/49] fix: temp sc filtering --- .../src/api/routes/admin/products/list-products.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 0342451112618..c4facc3baaa6b 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -373,12 +373,6 @@ async function listAndCountProductWithIsolatedProductModule( }, } - if (salesChannelIdFilter) { - query.product["sales_channels"]["__args"] = { - filters: { id: salesChannelIdFilter }, - } - } - let { rows: products, metadata: { count }, @@ -389,7 +383,12 @@ async function listAndCountProductWithIsolatedProductModule( }) if (salesChannelIdFilter) { - products = products.filter((product) => product.sales_channels?.length) + products = products.filter( + (product) => + !!product.sales_channels?.find((sc) => + salesChannelIdFilter.includes(sc.id) + ) + ) } return [products, count] From abcb96ca64874262bbd4f9b00fe80284290da6a8 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 16 Nov 2023 12:27:03 +0100 Subject: [PATCH 39/49] fix: use RQ to filter by SC --- .../api/routes/admin/products/list-products.ts | 15 +++++---------- .../api/routes/store/products/list-products.ts | 6 +----- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index c4facc3baaa6b..34addcebc7da1 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -373,7 +373,11 @@ async function listAndCountProductWithIsolatedProductModule( }, } - let { + if (salesChannelIdFilter) { + query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } + } + + const { rows: products, metadata: { count }, } = await remoteQuery(query) @@ -382,15 +386,6 @@ async function listAndCountProductWithIsolatedProductModule( product.profile_id = product.profile?.id }) - if (salesChannelIdFilter) { - products = products.filter( - (product) => - !!product.sales_channels?.find((sc) => - salesChannelIdFilter.includes(sc.id) - ) - ) - } - return [products, count] } diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index 17dd43ba9e94f..ed33a2cf57e76 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -403,7 +403,7 @@ async function listAndCountProductWithIsolatedProductModule( query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } } - let { + const { rows: products, metadata: { count }, } = await remoteQuery(query) @@ -412,10 +412,6 @@ async function listAndCountProductWithIsolatedProductModule( product.profile_id = product.profile?.id }) - if (salesChannelIdFilter) { - products = products.filter((product) => product.sales_channels?.length) - } - return [products, count] } From a8c5e959a80a4bebb67e4b92f58f087946c4dafa Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 4 Dec 2023 17:06:27 +0100 Subject: [PATCH 40/49] wip: models/migration/links --- .../link-modules/src/definitions/index.ts | 1 + .../src/definitions/order-sales-channel.ts | 66 ++++++++++++++++++ packages/link-modules/src/links.ts | 6 ++ .../src/joiner-configs/order-service.ts | 15 +++++ ...1698160217000-order-sales-channels-link.ts | 43 ++++++++++++ .../medusa/src/models/order-sales-channel.ts | 27 ++++++++ packages/medusa/src/models/order.ts | 67 +++++++++++++++++-- 7 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 packages/link-modules/src/definitions/order-sales-channel.ts create mode 100644 packages/medusa/src/joiner-configs/order-service.ts create mode 100644 packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts create mode 100644 packages/medusa/src/models/order-sales-channel.ts diff --git a/packages/link-modules/src/definitions/index.ts b/packages/link-modules/src/definitions/index.ts index 571e83bcedde3..11c74b689cdf2 100644 --- a/packages/link-modules/src/definitions/index.ts +++ b/packages/link-modules/src/definitions/index.ts @@ -3,3 +3,4 @@ export * from "./product-variant-inventory-item" export * from "./product-variant-price-set" export * from "./product-shipping-profile" export * from "./product-sales-channel" +export * from "./order-sales-channel" diff --git a/packages/link-modules/src/definitions/order-sales-channel.ts b/packages/link-modules/src/definitions/order-sales-channel.ts new file mode 100644 index 0000000000000..fd2b532616ca9 --- /dev/null +++ b/packages/link-modules/src/definitions/order-sales-channel.ts @@ -0,0 +1,66 @@ +import { ModuleJoinerConfig } from "@medusajs/types" + +import { LINKS } from "../links" + +export const OrderSalesChannel: ModuleJoinerConfig = { + serviceName: LINKS.OrderSalesChannel, + isLink: true, + databaseConfig: { + tableName: "order_sales_channel", + idPrefix: "ordersc", + }, + alias: [ + { + name: "order_sales_channel", + }, + { + name: "order_sales_channels", + }, + ], + primaryKeys: ["id", "order_id", "sales_channel_id"], + relationships: [ + { + serviceName: "orderService", + isInternalService: true, + primaryKey: "id", + foreignKey: "order_id", + alias: "order", + }, + { + serviceName: "salesChannelService", + isInternalService: true, + primaryKey: "id", + foreignKey: "sales_channel_id", + alias: "sales_channel", + }, + ], + extends: [ + { + serviceName: "orderService", + fieldAlias: { + sales_channel: "sales_channel_link.sales_channel", + }, + relationship: { + serviceName: LINKS.OrderSalesChannel, + isInternalService: true, + primaryKey: "order_id", + foreignKey: "id", + alias: "sales_channel_link", + }, + }, + { + serviceName: "salesChannelService", + fieldAlias: { + carts: "order_link.cart", + }, + relationship: { + serviceName: LINKS.OrderSalesChannel, + isInternalService: true, + primaryKey: "sales_channel_id", + foreignKey: "id", + alias: "order_link", + isList: true, + }, + }, + ], +} diff --git a/packages/link-modules/src/links.ts b/packages/link-modules/src/links.ts index bda65e6f2ccba..02d4e2ac6ce21 100644 --- a/packages/link-modules/src/links.ts +++ b/packages/link-modules/src/links.ts @@ -28,4 +28,10 @@ export const LINKS = { "salesChannelService", "sales_channel_id" ), + OrderSalesChannel: composeLinkName( + "orderService", + "order_id", + "salesChannelService", + "sales_channel_id" + ), } diff --git a/packages/medusa/src/joiner-configs/order-service.ts b/packages/medusa/src/joiner-configs/order-service.ts new file mode 100644 index 0000000000000..3a5e3dc5feb6f --- /dev/null +++ b/packages/medusa/src/joiner-configs/order-service.ts @@ -0,0 +1,15 @@ +import { ModuleJoinerConfig } from "@medusajs/types" + +export default { + serviceName: "orderService", + primaryKeys: ["id"], + linkableKeys: { order_id: "Order" }, + alias: [ + { + name: "order", + }, + { + name: "orders", + }, + ], +} as ModuleJoinerConfig diff --git a/packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts b/packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts new file mode 100644 index 0000000000000..06fa7c8cfcd9c --- /dev/null +++ b/packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts @@ -0,0 +1,43 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import { MedusaV2Flag } from "@medusajs/utils" + +import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels" + +export const featureFlag = [SalesChannelFeatureFlag.key, MedusaV2Flag.key] + +export class OrderSalesChannelLink1698160217000 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS "order_sales_channel" + ( + "id" character varying NOT NULL, + "order_id" character varying NOT NULL, + "sales_channel_id" character varying NOT NULL, + "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "deleted_at" TIMESTAMP WITH TIME ZONE, + CONSTRAINT "order_sales_channel_pk" PRIMARY KEY ("order_id", "sales_channel_id"), + CONSTRAINT "order_sales_channel_order_id_unique" UNIQUE ("order_id") + ); + CREATE INDEX IF NOT EXISTS "IDX_id_order_sales_channel" ON "order_sales_channel" ("id"); + + insert into "order_sales_channel" (id, order_id, sales_channel_id) + (select 'ordersc_' || substr(md5(random()::text), 0, 27), id, sales_channel_id from "order" WHERE sales_channel_id IS NOT NULL); + + ALTER TABLE "order" DROP CONSTRAINT IF EXISTS "FK_6ff7e874f01b478c115fdd462eb"; + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + UPDATE "order" + SET "sales_channel_id" = "order_sales_channel"."sales_channel_id" + FROM "order_sales_channel" + WHERE "order"."id" = "order_sales_channel"."order_id"; + + DROP TABLE IF EXISTS "order_sales_channel"; + + ALTER TABLE "order" ADD CONSTRAINT "FK_6ff7e874f01b478c115fdd462eb" FOREIGN KEY ("sales_channel_id") REFERENCES "sales_channel"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + `) + } +} diff --git a/packages/medusa/src/models/order-sales-channel.ts b/packages/medusa/src/models/order-sales-channel.ts new file mode 100644 index 0000000000000..48e956516ab87 --- /dev/null +++ b/packages/medusa/src/models/order-sales-channel.ts @@ -0,0 +1,27 @@ +import { BeforeInsert, Column, Index } from "typeorm" +import { MedusaV2Flag } from "@medusajs/utils" + +import { generateEntityId } from "../utils" +import { SoftDeletableEntity } from "../interfaces" +import { FeatureFlagEntity } from "../utils/feature-flag-decorators" + +@FeatureFlagEntity([MedusaV2Flag.key, "sales_channels"]) +export class OrderSalesChannel extends SoftDeletableEntity { + @Index("order_sales_channel_order_id_unique", { + unique: true, + }) + @Column() + order_id: string + + @Column() + sales_channel_id: string + + /** + * @apiIgnore + */ + // NOTE: not working + @BeforeInsert() + private beforeInsert(): void { + this.id = generateEntityId(this.id, "ordersc") + } +} diff --git a/packages/medusa/src/models/order.ts b/packages/medusa/src/models/order.ts index 7891b53748479..c5141e4cfbb1e 100644 --- a/packages/medusa/src/models/order.ts +++ b/packages/medusa/src/models/order.ts @@ -1,5 +1,7 @@ import { + AfterLoad, BeforeInsert, + BeforeUpdate, Column, Entity, Generated, @@ -12,7 +14,10 @@ import { OneToOne, } from "typeorm" import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column" -import { FeatureFlagColumn, FeatureFlagDecorators, } from "../utils/feature-flag-decorators" +import { + FeatureFlagColumn, + FeatureFlagDecorators, +} from "../utils/feature-flag-decorators" import { BaseEntity } from "../interfaces/models/base-entity" import { generateEntityId } from "../utils/generate-entity-id" @@ -36,10 +41,11 @@ import { Return } from "./return" import { SalesChannel } from "./sales-channel" import { ShippingMethod } from "./shipping-method" import { Swap } from "./swap" +import { MedusaV2Flag } from "@medusajs/utils" /** * @enum - * + * * The order's status. */ export enum OrderStatus { @@ -48,7 +54,7 @@ export enum OrderStatus { */ PENDING = "pending", /** - * The order is completed, meaning that + * The order is completed, meaning that * the items have been fulfilled and the payment * has been captured. */ @@ -69,7 +75,7 @@ export enum OrderStatus { /** * @enum - * + * * The order's fulfillment status. */ export enum FulfillmentStatus { @@ -78,7 +84,7 @@ export enum FulfillmentStatus { */ NOT_FULFILLED = "not_fulfilled", /** - * Some of the order's items, but not all, are fulfilled. + * Some of the order's items, but not all, are fulfilled. */ PARTIALLY_FULFILLED = "partially_fulfilled", /** @@ -113,7 +119,7 @@ export enum FulfillmentStatus { /** * @enum - * + * * The order's payment status. */ export enum PaymentStatus { @@ -321,6 +327,25 @@ export class Order extends BaseEntity { ]) sales_channel: SalesChannel + @FeatureFlagDecorators( + [MedusaV2Flag.key, "sales_channels"], + [ + ManyToMany(() => SalesChannel, { cascade: ["remove", "soft-remove"] }), + JoinTable({ + name: "order_sales_channel", + joinColumn: { + name: "cart_id", + referencedColumnName: "id", + }, + inverseJoinColumn: { + name: "sales_channel_id", + referencedColumnName: "id", + }, + }), + ] + ) + sales_channels?: SalesChannel[] + // Total fields shipping_total: number shipping_tax_total: number | null @@ -345,6 +370,12 @@ export class Order extends BaseEntity { private async beforeInsert(): Promise { this.id = generateEntityId(this.id, "order") + if (this.sales_channel_id || this.sales_channel) { + this.sales_channels = [ + { id: this.sales_channel_id || this.sales_channel?.id }, + ] as SalesChannel[] + } + if (process.env.NODE_ENV === "development" && !this.display_id) { const disId = await manualAutoIncrement("order") @@ -353,6 +384,30 @@ export class Order extends BaseEntity { } } } + + /** + * @apiIgnore + */ + @BeforeUpdate() + private beforeUpdate(): void { + if (this.sales_channel_id || this.sales_channel) { + this.sales_channels = [ + { id: this.sales_channel_id || this.sales_channel?.id }, + ] as SalesChannel[] + } + } + + /** + * @apiIgnore + */ + @AfterLoad() + private afterLoad(): void { + if (this.sales_channels) { + this.sales_channel = this.sales_channels?.[0] + this.sales_channel_id = this.sales_channel?.id + delete this.sales_channels + } + } } /** From b6b32483a866f3d340d4d7e6a7894d9fd999a575 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 5 Dec 2023 13:57:42 +0100 Subject: [PATCH 41/49] feat: service changes, add to Orders to SC model, FF array --- packages/medusa/src/models/sales-channel.ts | 26 ++++++++++++++++++- packages/medusa/src/services/order.ts | 25 +++++++++++++++++- .../src/utils/feature-flag-decorators.ts | 6 ++--- .../src/feature-flags/utils/flag-router.ts | 8 +++++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/packages/medusa/src/models/sales-channel.ts b/packages/medusa/src/models/sales-channel.ts index a25bc2e7f8f05..7fe637f7561f7 100644 --- a/packages/medusa/src/models/sales-channel.ts +++ b/packages/medusa/src/models/sales-channel.ts @@ -1,10 +1,15 @@ import { BeforeInsert, Column, JoinTable, ManyToMany, OneToMany } from "typeorm" -import { FeatureFlagEntity } from "../utils/feature-flag-decorators" +import { + FeatureFlagDecorators, + FeatureFlagEntity, +} from "../utils/feature-flag-decorators" import { SoftDeletableEntity } from "../interfaces" import { DbAwareColumn, generateEntityId } from "../utils" import { SalesChannelLocation } from "./sales-channel-location" import { Product } from "./product" +import { MedusaV2Flag } from "@medusajs/utils" +import { Order } from "./order" @FeatureFlagEntity("sales_channels") export class SalesChannel extends SoftDeletableEntity { @@ -34,6 +39,25 @@ export class SalesChannel extends SoftDeletableEntity { }) products: Product[] + @FeatureFlagDecorators( + [MedusaV2Flag.key, "sales_channels"], + [ + ManyToMany(() => Order), + JoinTable({ + name: "order_sales_channel", + joinColumn: { + name: "sales_channel_id", + referencedColumnName: "id", + }, + inverseJoinColumn: { + name: "order_id", + referencedColumnName: "id", + }, + }), + ] + ) + orders: Order[] + @OneToMany( () => SalesChannelLocation, (scLocation) => scLocation.sales_channel, diff --git a/packages/medusa/src/services/order.ts b/packages/medusa/src/services/order.ts index 59b61faa86170..56fca682d8e2a 100644 --- a/packages/medusa/src/services/order.ts +++ b/packages/medusa/src/services/order.ts @@ -5,6 +5,7 @@ import { FlagRouter, isDefined, MedusaError, + MedusaV2Flag, promiseAll, } from "@medusajs/utils" import { @@ -64,6 +65,7 @@ import { TotalsContext, UpdateOrderInput } from "../types/orders" import { CreateShippingMethodDto } from "../types/shipping-options" import { buildQuery, isString, setMetadata } from "../utils" import EventBusService from "./event-bus" +import { RemoteLink } from "@medusajs/modules-sdk" export const ORDER_CART_ALREADY_EXISTS_ERROR = "Order from cart already exists" @@ -90,6 +92,7 @@ type InjectedDependencies = { eventBusService: EventBusService featureFlagRouter: FlagRouter productVariantInventoryService: ProductVariantInventoryService + remoteLink: RemoteLink } class OrderService extends TransactionBaseService { @@ -132,6 +135,7 @@ class OrderService extends TransactionBaseService { protected readonly inventoryService_: IInventoryService protected readonly eventBus_: EventBusService protected readonly featureFlagRouter_: FlagRouter + protected remoteLink_: RemoteLink // eslint-disable-next-line max-len protected readonly productVariantInventoryService_: ProductVariantInventoryService @@ -150,6 +154,7 @@ class OrderService extends TransactionBaseService { taxProviderService, regionService, cartService, + remoteLink, addressRepository, giftCardService, draftOrderService, @@ -180,6 +185,7 @@ class OrderService extends TransactionBaseService { this.draftOrderService_ = draftOrderService this.featureFlagRouter_ = featureFlagRouter this.productVariantInventoryService_ = productVariantInventoryService + this.remoteLink_ = remoteLink } /** @@ -694,7 +700,8 @@ class OrderService extends TransactionBaseService { if ( cart.sales_channel_id && - this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) + this.featureFlagRouter_.isFeatureEnabled(SalesChannelFeatureFlag.key) && + !this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key) ) { toCreate.sales_channel_id = cart.sales_channel_id } @@ -711,6 +718,22 @@ class OrderService extends TransactionBaseService { const rawOrder = orderRepo.create(toCreate) const order = await orderRepo.save(rawOrder) + if ( + this.featureFlagRouter_.isFeatureEnabled([ + SalesChannelFeatureFlag.key, + MedusaV2Flag.key, + ]) + ) { + await this.remoteLink_.create({ + orderService: { + order_id: order.id, + }, + salesChannelService: { + sales_channel_id: cart.sales_channel_id as string, + }, + }) + } + if (total !== 0 && payment) { await this.paymentProviderService_ .withTransaction(manager) diff --git a/packages/medusa/src/utils/feature-flag-decorators.ts b/packages/medusa/src/utils/feature-flag-decorators.ts index ddfb45a6337eb..472d14a2d197b 100644 --- a/packages/medusa/src/utils/feature-flag-decorators.ts +++ b/packages/medusa/src/utils/feature-flag-decorators.ts @@ -34,7 +34,7 @@ export function FeatureFlagColumn( } export function FeatureFlagDecorators( - featureFlag: string, + featureFlag: string | string[], decorators: PropertyDecorator[] ): PropertyDecorator { return function (target, propertyName) { @@ -51,7 +51,7 @@ export function FeatureFlagDecorators( } export function FeatureFlagClassDecorators( - featureFlag: string, + featureFlag: string | string[], decorators: ClassDecorator[] ): ClassDecorator { return function (target) { @@ -68,7 +68,7 @@ export function FeatureFlagClassDecorators( } export function FeatureFlagEntity( - featureFlag: string, + featureFlag: string | string[], name?: string, options?: EntityOptions ): ClassDecorator { diff --git a/packages/utils/src/feature-flags/utils/flag-router.ts b/packages/utils/src/feature-flags/utils/flag-router.ts index 734c38e1827e0..1efce2436a57d 100644 --- a/packages/utils/src/feature-flags/utils/flag-router.ts +++ b/packages/utils/src/feature-flags/utils/flag-router.ts @@ -19,11 +19,17 @@ export class FlagRouter implements FeatureFlagTypes.IFlagRouter { * @param flag - The flag to check * @return {boolean} - Whether the flag is enabled or not */ - public isFeatureEnabled(flag: string | Record): boolean { + public isFeatureEnabled( + flag: string | string[] | Record + ): boolean { if (isString(flag)) { return !!this.flags[flag] } + if (Array.isArray(flag)) { + return flag.every((f) => !!this.flags[f]) + } + if (isObject(flag)) { const [nestedFlag, value] = Object.entries(flag)[0] From 96263e8c1adb9b1085adec6bb1de94eb0aa567c8 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 5 Dec 2023 14:03:51 +0100 Subject: [PATCH 42/49] fix: typo --- packages/link-modules/src/definitions/order-sales-channel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/link-modules/src/definitions/order-sales-channel.ts b/packages/link-modules/src/definitions/order-sales-channel.ts index fd2b532616ca9..5ddc4678b1e4e 100644 --- a/packages/link-modules/src/definitions/order-sales-channel.ts +++ b/packages/link-modules/src/definitions/order-sales-channel.ts @@ -51,7 +51,7 @@ export const OrderSalesChannel: ModuleJoinerConfig = { { serviceName: "salesChannelService", fieldAlias: { - carts: "order_link.cart", + orders: "order_link.order", }, relationship: { serviceName: LINKS.OrderSalesChannel, From 34bf7218ee786f5f926aacb647be9bc724aa0704 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 6 Dec 2023 10:13:02 +0100 Subject: [PATCH 43/49] fix: order service units --- packages/medusa/src/services/__tests__/order.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index d51d3f2fdac8d..31ac656aa297e 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -4,6 +4,7 @@ import { newTotalsServiceMock } from "../__mocks__/new-totals" import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory" import { taxProviderServiceMock } from "../__mocks__/tax-provider" import OrderService from "../order" +import { FlagRouter } from "@medusajs/utils" describe("OrderService", () => { const totalsService = { @@ -151,6 +152,7 @@ describe("OrderService", () => { eventBusService, cartService, productVariantInventoryService, + featureFlagRouter: new FlagRouter({}), }) beforeEach(async () => { From b96f4ece2ca2e72f2843c1ab13715ecfe5f0977a Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 6 Dec 2023 10:16:54 +0100 Subject: [PATCH 44/49] fix: sendgrid snaps --- .../medusa-plugin-sendgrid/__snapshots__/index.js.snap | 4 +++- packages/medusa/src/services/__tests__/order.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap b/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap index f4bc04256014d..313a75c800046 100644 --- a/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap +++ b/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap @@ -753,7 +753,9 @@ Object { exports[`medusa-plugin-sendgrid order canceled data 1`] = ` Object { + "afterLoad": [Function], "beforeInsert": [Function], + "beforeUpdate": [Function], "billing_address": null, "billing_address_id": null, "canceled_at": Any, @@ -2533,4 +2535,4 @@ Object { "tracking_links": Array [], "tracking_number": "", } -`; \ No newline at end of file +`; diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index 31ac656aa297e..3e28b46b24dbc 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -1,10 +1,10 @@ import { IdMap, MockManager, MockRepository } from "medusa-test-utils" +import { FlagRouter } from "@medusajs/utils" import { LineItemServiceMock } from "../__mocks__/line-item" import { newTotalsServiceMock } from "../__mocks__/new-totals" import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory" import { taxProviderServiceMock } from "../__mocks__/tax-provider" import OrderService from "../order" -import { FlagRouter } from "@medusajs/utils" describe("OrderService", () => { const totalsService = { From 8960dbe7b82eb10b8957999b4978c37d6005f48e Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 6 Dec 2023 11:15:15 +0100 Subject: [PATCH 45/49] fix: remaining snaps --- .../medusa-plugin-sendgrid/__snapshots__/index.js.snap | 10 ++++++++++ .../plugins/__tests__/medusa-plugin-sendgrid/index.js | 5 +---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap b/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap index 313a75c800046..30f77a12fb669 100644 --- a/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap +++ b/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/__snapshots__/index.js.snap @@ -425,7 +425,9 @@ Object { ], "locale": null, "order": Object { + "afterLoad": [Function], "beforeInsert": [Function], + "beforeUpdate": [Function], "billing_address_id": null, "canceled_at": null, "cart_id": null, @@ -984,7 +986,9 @@ Object { exports[`medusa-plugin-sendgrid order placed data 1`] = ` Object { + "afterLoad": [Function], "beforeInsert": [Function], + "beforeUpdate": [Function], "billing_address": null, "billing_address_id": null, "canceled_at": null, @@ -1238,7 +1242,9 @@ Object { }, "locale": null, "order": Object { + "afterLoad": [Function], "beforeInsert": [Function], + "beforeUpdate": [Function], "billing_address": null, "billing_address_id": null, "canceled_at": null, @@ -1614,7 +1620,9 @@ Object { ], "locale": null, "order": Object { + "afterLoad": [Function], "beforeInsert": [Function], + "beforeUpdate": [Function], "billing_address_id": null, "canceled_at": null, "cart_id": null, @@ -2080,7 +2088,9 @@ Object { ], "locale": null, "order": Object { + "afterLoad": [Function], "beforeInsert": [Function], + "beforeUpdate": [Function], "billing_address_id": null, "canceled_at": null, "cart_id": null, diff --git a/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/index.js b/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/index.js index 06f628ceb739e..bc29f61351c94 100644 --- a/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/index.js +++ b/integration-tests/plugins/__tests__/medusa-plugin-sendgrid/index.js @@ -4,10 +4,7 @@ const { startBootstrapApp, } = require("../../../environment-helpers/bootstrap-app") const { initDb, useDb } = require("../../../environment-helpers/use-db") -const { - useApi, - useExpressServer, -} = require("../../../environment-helpers/use-api") +const { useApi } = require("../../../environment-helpers/use-api") const adminSeeder = require("../../../helpers/admin-seeder") From 50bf9c28e42ba6d2397ef9861002e115ed59c225 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 6 Dec 2023 11:59:51 +0100 Subject: [PATCH 46/49] fix: rename migration --- ...nnels-link.ts => 1701860329931-order-sales-channels-link.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename packages/medusa/src/migrations/{1698160217000-order-sales-channels-link.ts => 1701860329931-order-sales-channels-link.ts} (96%) diff --git a/packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts b/packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts similarity index 96% rename from packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts rename to packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts index 06fa7c8cfcd9c..f89b1de8c62de 100644 --- a/packages/medusa/src/migrations/1698160217000-order-sales-channels-link.ts +++ b/packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts @@ -5,7 +5,7 @@ import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels" export const featureFlag = [SalesChannelFeatureFlag.key, MedusaV2Flag.key] -export class OrderSalesChannelLink1698160217000 implements MigrationInterface { +export class OrderSalesChannelLink1701860329931 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` CREATE TABLE IF NOT EXISTS "order_sales_channel" From f63e72d62521868801d13f36baee20918c8c00c0 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 6 Dec 2023 12:43:45 +0100 Subject: [PATCH 47/49] chore: changesets --- .changeset/swift-carpets-count.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/swift-carpets-count.md diff --git a/.changeset/swift-carpets-count.md b/.changeset/swift-carpets-count.md new file mode 100644 index 0000000000000..5df38898bb6c2 --- /dev/null +++ b/.changeset/swift-carpets-count.md @@ -0,0 +1,8 @@ +--- +"@medusajs/link-modules": patch +"@medusajs/core-flows": patch +"@medusajs/medusa": patch +"@medusajs/utils": patch +--- + +feat: sales channel <> order link From ea3cad74ed3bffeec5cb00e2d00a66cffaab5ac7 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 2 Jan 2024 13:03:14 +0100 Subject: [PATCH 48/49] fix: address some comments --- .../handlers/product/detach-sales-channel-from-products.ts | 7 ++----- packages/medusa/src/models/order-sales-channel.ts | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/core-flows/src/handlers/product/detach-sales-channel-from-products.ts b/packages/core-flows/src/handlers/product/detach-sales-channel-from-products.ts index 72ecdcc1aa2fa..31d029ab9ec1a 100644 --- a/packages/core-flows/src/handlers/product/detach-sales-channel-from-products.ts +++ b/packages/core-flows/src/handlers/product/detach-sales-channel-from-products.ts @@ -38,14 +38,13 @@ export async function detachSalesChannelFromProducts({ if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) { const remoteLink = container.resolve("remoteLink") - const promises: Promise[] = [] for (const [ salesChannelId, productIds, ] of salesChannelIdProductIdsMap.entries()) { - productIds.forEach((id) => - promises.push( + await promiseAll( + productIds.map((id) => remoteLink.dismiss({ [Modules.PRODUCT]: { product_id: id, @@ -57,8 +56,6 @@ export async function detachSalesChannelFromProducts({ ) ) } - - return } else { await promiseAll( Array.from(salesChannelIdProductIdsMap.entries()).map( diff --git a/packages/medusa/src/models/order-sales-channel.ts b/packages/medusa/src/models/order-sales-channel.ts index 48e956516ab87..0a6082bade610 100644 --- a/packages/medusa/src/models/order-sales-channel.ts +++ b/packages/medusa/src/models/order-sales-channel.ts @@ -1,11 +1,11 @@ import { BeforeInsert, Column, Index } from "typeorm" -import { MedusaV2Flag } from "@medusajs/utils" +import { MedusaV2Flag, SalesChannelFeatureFlag } from "@medusajs/utils" import { generateEntityId } from "../utils" import { SoftDeletableEntity } from "../interfaces" import { FeatureFlagEntity } from "../utils/feature-flag-decorators" -@FeatureFlagEntity([MedusaV2Flag.key, "sales_channels"]) +@FeatureFlagEntity([MedusaV2Flag.key, SalesChannelFeatureFlag.key]) export class OrderSalesChannel extends SoftDeletableEntity { @Index("order_sales_channel_order_id_unique", { unique: true, @@ -19,7 +19,6 @@ export class OrderSalesChannel extends SoftDeletableEntity { /** * @apiIgnore */ - // NOTE: not working @BeforeInsert() private beforeInsert(): void { this.id = generateEntityId(this.id, "ordersc") From 7294108ac1d7c9187a58e76d4c329c4efd2935da Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 2 Jan 2024 13:21:26 +0100 Subject: [PATCH 49/49] fix: update entity --- .../1701860329931-order-sales-channels-link.ts | 2 +- packages/medusa/src/models/order-sales-channel.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts b/packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts index f89b1de8c62de..d9477d4094a7c 100644 --- a/packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts +++ b/packages/medusa/src/migrations/1701860329931-order-sales-channels-link.ts @@ -11,7 +11,7 @@ export class OrderSalesChannelLink1701860329931 implements MigrationInterface { CREATE TABLE IF NOT EXISTS "order_sales_channel" ( "id" character varying NOT NULL, - "order_id" character varying NOT NULL, + "order_id" character varying NOT NULL, "sales_channel_id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), diff --git a/packages/medusa/src/models/order-sales-channel.ts b/packages/medusa/src/models/order-sales-channel.ts index 0a6082bade610..497f464169871 100644 --- a/packages/medusa/src/models/order-sales-channel.ts +++ b/packages/medusa/src/models/order-sales-channel.ts @@ -1,4 +1,4 @@ -import { BeforeInsert, Column, Index } from "typeorm" +import { BeforeInsert, Column, Index, PrimaryColumn } from "typeorm" import { MedusaV2Flag, SalesChannelFeatureFlag } from "@medusajs/utils" import { generateEntityId } from "../utils" @@ -7,13 +7,16 @@ import { FeatureFlagEntity } from "../utils/feature-flag-decorators" @FeatureFlagEntity([MedusaV2Flag.key, SalesChannelFeatureFlag.key]) export class OrderSalesChannel extends SoftDeletableEntity { + @Column() + id: string + @Index("order_sales_channel_order_id_unique", { unique: true, }) - @Column() + @PrimaryColumn() order_id: string - @Column() + @PrimaryColumn() sales_channel_id: string /**