From f4f6bb3a813ed0b532a7f729857b2025ee7d3b42 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 19 Oct 2023 11:51:40 +0200 Subject: [PATCH 01/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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 87fa07bab78e121196d92310a323471adfcf0a65 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 15 Dec 2023 11:03:06 +0100 Subject: [PATCH 40/40] fix: add sc to filter to list --- .../utils/queries/products/list-products.ts | 47 +++++++------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/packages/medusa/src/utils/queries/products/list-products.ts b/packages/medusa/src/utils/queries/products/list-products.ts index 5bc2402b1bc49..117fe0dde4611 100644 --- a/packages/medusa/src/utils/queries/products/list-products.ts +++ b/packages/medusa/src/utils/queries/products/list-products.ts @@ -1,7 +1,7 @@ import { MedusaContainer } from "@medusajs/types" import { MedusaV2Flag, promiseAll } from "@medusajs/utils" -import { PriceListService, SalesChannelService } from "../../../services" +import { PriceListService } from "../../../services" import { getVariantsFromPriceList } from "./get-variants-from-price-list" export async function listProducts( @@ -23,35 +23,6 @@ export async function listProducts( const salesChannelIdFilter = filterableFields.sales_channel_id delete filterableFields.sales_channel_id - if (salesChannelIdFilter) { - const salesChannelService = container.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 @@ -112,6 +83,10 @@ export async function listProducts( }, } + if (salesChannelIdFilter) { + query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter } + } + const { rows: products, metadata: { count }, @@ -245,4 +220,16 @@ 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", + ], + }, }