feat(medusa, medusa-js, medusa-react): Bulk add Products to a SalesChannel (#1833)
This commit is contained in:
committed by
GitHub
parent
cdd91974f9
commit
f35ea5156a
@@ -6,7 +6,6 @@ const { useApi } = require("../../../helpers/use-api")
|
||||
const { useDb } = require("../../../helpers/use-db")
|
||||
|
||||
const adminSeeder = require("../../helpers/admin-seeder")
|
||||
|
||||
const {
|
||||
simpleSalesChannelFactory,
|
||||
simpleProductFactory,
|
||||
@@ -744,4 +743,72 @@ describe("sales channels", () => {
|
||||
expect(attachedProduct.sales_channels.length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/sales-channels/:id/products/batch", () => {
|
||||
let salesChannel
|
||||
let product
|
||||
|
||||
beforeEach(async() => {
|
||||
try {
|
||||
await adminSeeder(dbConnection)
|
||||
salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "test name",
|
||||
description: "test description",
|
||||
})
|
||||
product = await simpleProductFactory(dbConnection, {
|
||||
id: "product_1",
|
||||
title: "test title",
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("should add products to a sales channel", async() => {
|
||||
const api = useApi()
|
||||
|
||||
const payload = {
|
||||
product_ids: [{ id: product.id }]
|
||||
}
|
||||
|
||||
let response = await api.post(
|
||||
`/admin/sales-channels/${salesChannel.id}/products/batch`,
|
||||
payload,
|
||||
adminReqConfig
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.sales_channel).toEqual({
|
||||
id: expect.any(String),
|
||||
name: "test name",
|
||||
description: "test description",
|
||||
is_disabled: false,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
})
|
||||
|
||||
let attachedProduct = await dbConnection.manager.findOne(Product, {
|
||||
where: { id: product.id },
|
||||
relations: ["sales_channels"]
|
||||
})
|
||||
|
||||
expect(attachedProduct.sales_channels.length).toBe(1)
|
||||
expect(attachedProduct.sales_channels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
name: "test name",
|
||||
description: "test description",
|
||||
is_disabled: false,
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
AdminSalesChannelsDeleteRes,
|
||||
AdminSalesChannelsListRes,
|
||||
AdminDeleteSalesChannelsChannelProductsBatchReq,
|
||||
AdminPostSalesChannelsChannelProductsBatchReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { ResponsePromise } from "../../typings"
|
||||
import BaseResource from "../base"
|
||||
@@ -104,6 +105,22 @@ class AdminSalesChannelsResource extends BaseResource {
|
||||
const path = `/admin/sales-channels/${salesChannelId}/products/batch`
|
||||
return this.client.request("DELETE", path, payload, {}, customHeaders)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add products to a sales channel
|
||||
* @experimental This feature is under development and may change in the future.
|
||||
* To use this feature please enable featureflag `sales_channels` in your medusa backend project.
|
||||
* @description Add products to a sales channel
|
||||
* @returns a medusa sales channel
|
||||
*/
|
||||
addProducts(
|
||||
salesChannelId: string,
|
||||
payload: AdminPostSalesChannelsChannelProductsBatchReq,
|
||||
customHeaders: Record<string, any> = {}
|
||||
): ResponsePromise<AdminSalesChannelsRes> {
|
||||
const path = `/admin/sales-channels/${salesChannelId}/products/batch`
|
||||
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminSalesChannelsResource
|
||||
|
||||
@@ -1735,4 +1735,13 @@ export const adminHandlers = [
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/admin/sales-channels/:id/products/batch", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
sales_channel: fixtures.get("sales_channel"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
]
|
||||
|
||||
@@ -3,7 +3,8 @@ import {
|
||||
AdminSalesChannelsRes,
|
||||
AdminPostSalesChannelsSalesChannelReq,
|
||||
AdminSalesChannelsDeleteRes,
|
||||
AdminDeleteSalesChannelsChannelProductsBatchReq
|
||||
AdminDeleteSalesChannelsChannelProductsBatchReq,
|
||||
AdminPostSalesChannelsChannelProductsBatchReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useMutation, UseMutationOptions, useQueryClient } from "react-query"
|
||||
@@ -117,4 +118,34 @@ export const useAdminDeleteProductsFromSalesChannel = (
|
||||
options
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add products to a sales channel
|
||||
* @experimental This feature is under development and may change in the future.
|
||||
* To use this feature please enable featureflag `sales_channels` in your medusa backend project.
|
||||
* @description Add products to a sales channel
|
||||
* @param id
|
||||
* @param options
|
||||
*/
|
||||
export const useAdminAddProductsToSalesChannel = (
|
||||
id: string,
|
||||
options?: UseMutationOptions<
|
||||
Response<AdminSalesChannelsRes>,
|
||||
Error,
|
||||
AdminPostSalesChannelsChannelProductsBatchReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation(
|
||||
(payload: AdminPostSalesChannelsChannelProductsBatchReq) => {
|
||||
return client.admin.salesChannels.addProducts(id, payload)
|
||||
},
|
||||
buildOptions(
|
||||
queryClient,
|
||||
[adminSalesChannelsKeys.lists(), adminSalesChannelsKeys.detail(id)],
|
||||
options
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useAdminCreateSalesChannel,
|
||||
useAdminUpdateSalesChannel,
|
||||
useAdminDeleteProductsFromSalesChannel,
|
||||
useAdminAddProductsToSalesChannel,
|
||||
} from "../../../../src"
|
||||
import { fixtures } from "../../../../mocks/data"
|
||||
import { createWrapper } from "../../../utils"
|
||||
@@ -107,3 +108,25 @@ describe("useAdminDeleteProductsFromSalesChannel hook", () => {
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
describe("useAdminAddProductsToSalesChannel hook", () => {
|
||||
test("add products to a sales channel", async () => {
|
||||
const id = fixtures.get("sales_channel").id
|
||||
const productId = fixtures.get("product").id
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useAdminAddProductsToSalesChannel(id),
|
||||
{ wrapper: createWrapper() }
|
||||
)
|
||||
|
||||
result.current.mutate({ product_ids: [
|
||||
{ id: productId }
|
||||
]})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data).toEqual(expect.objectContaining({
|
||||
sales_channel: fixtures.get("sales_channel"),
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { SalesChannelServiceMock } from "../../../../../services/__mocks__/sales-channel"
|
||||
|
||||
describe("POST /admin/sales-channels/:id/products/batch", () => {
|
||||
describe("add product to a sales channel", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/sales-channels/${IdMap.getId("sales_channel_1")}/products/batch`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
payload: {
|
||||
product_ids: [{ id: IdMap.getId("sales_channel_1_product_1") }]
|
||||
},
|
||||
flags: ["sales_channels"],
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls the retrieve method from the sales channel service", () => {
|
||||
expect(SalesChannelServiceMock.addProducts).toHaveBeenCalledTimes(1)
|
||||
expect(SalesChannelServiceMock.addProducts).toHaveBeenCalledWith(
|
||||
IdMap.getId("sales_channel_1"),
|
||||
[IdMap.getId("sales_channel_1_product_1")]
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Request, Response } from "express"
|
||||
import { SalesChannelService } from "../../../../services"
|
||||
import { IsArray, ValidateNested } from "class-validator"
|
||||
import { Type } from "class-transformer"
|
||||
import { ProductBatchSalesChannel } from "../../../../types/sales-channels"
|
||||
|
||||
/**
|
||||
* @oas [post] /sales-channels/{id}/products/batch
|
||||
* operationId: "PostSalesChannelsChannelProductsBatch"
|
||||
* summary: "Assign a batch of product to a sales channel"
|
||||
* description: "Assign a batch of product to a sales channel."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Sales channel.
|
||||
* - (body) product_ids=* {ProductBatchSalesChannel} The product ids that must be assigned to the sales channel.
|
||||
* tags:
|
||||
* - Sales Channel
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* sales_channel:
|
||||
* $ref: "#/components/schemas/sales_channel"
|
||||
*/
|
||||
export default async (req: Request, res: Response): Promise<void> => {
|
||||
const { id } = req.params
|
||||
|
||||
const salesChannelService: SalesChannelService = req.scope.resolve(
|
||||
"salesChannelService"
|
||||
)
|
||||
|
||||
const validatedBody =
|
||||
req.validatedBody as AdminPostSalesChannelsChannelProductsBatchReq
|
||||
const salesChannel = await salesChannelService.addProducts(
|
||||
id,
|
||||
validatedBody.product_ids.map((p) => p.id)
|
||||
)
|
||||
res.status(200).json({ sales_channel: salesChannel })
|
||||
}
|
||||
|
||||
export class AdminPostSalesChannelsChannelProductsBatchReq {
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ProductBatchSalesChannel)
|
||||
product_ids: ProductBatchSalesChannel[]
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { AdminPostSalesChannelsSalesChannelReq } from "./update-sales-channel"
|
||||
import { AdminPostSalesChannelsReq } from "./create-sales-channel"
|
||||
import { AdminGetSalesChannelsParams } from "./list-sales-channels"
|
||||
import { AdminDeleteSalesChannelsChannelProductsBatchReq } from "./delete-products-batch"
|
||||
import { AdminPostSalesChannelsChannelProductsBatchReq } from "./add-product-batch"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -32,6 +33,11 @@ export default (app) => {
|
||||
"/",
|
||||
middlewares.wrap(require("./get-sales-channel").default)
|
||||
)
|
||||
salesChannelRouter.post(
|
||||
"/",
|
||||
transformBody(AdminPostSalesChannelsSalesChannelReq),
|
||||
middlewares.wrap(require("./update-sales-channel").default)
|
||||
)
|
||||
salesChannelRouter.delete(
|
||||
"/",
|
||||
middlewares.wrap(require("./delete-sales-channel").default)
|
||||
@@ -46,6 +52,11 @@ export default (app) => {
|
||||
transformBody(AdminDeleteSalesChannelsChannelProductsBatchReq),
|
||||
middlewares.wrap(require("./delete-products-batch").default)
|
||||
)
|
||||
salesChannelRouter.post(
|
||||
"/products/batch",
|
||||
transformBody(AdminPostSalesChannelsChannelProductsBatchReq),
|
||||
middlewares.wrap(require("./add-product-batch").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/",
|
||||
@@ -53,12 +64,6 @@ export default (app) => {
|
||||
middlewares.wrap(require("./create-sales-channel").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id",
|
||||
transformBody(AdminPostSalesChannelsSalesChannelReq),
|
||||
middlewares.wrap(require("./update-sales-channel").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -68,10 +73,6 @@ export type AdminSalesChannelsRes = {
|
||||
|
||||
export type AdminSalesChannelsDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminSalesChannelListRes = PaginatedResponse & {
|
||||
sales_channels: SalesChannel[]
|
||||
}
|
||||
|
||||
export type AdminSalesChannelsListRes = PaginatedResponse & {
|
||||
sales_channels: SalesChannel[]
|
||||
}
|
||||
@@ -82,3 +83,4 @@ export * from "./list-sales-channels"
|
||||
export * from "./update-sales-channel"
|
||||
export * from "./delete-sales-channel"
|
||||
export * from "./delete-products-batch"
|
||||
export * from "./add-product-batch"
|
||||
|
||||
@@ -44,4 +44,21 @@ export class SalesChannelRepository extends Repository<SalesChannel> {
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async addProducts(
|
||||
salesChannelId: string,
|
||||
productIds: string[]
|
||||
): Promise<void> {
|
||||
await this.createQueryBuilder()
|
||||
.insert()
|
||||
.into("product_sales_channel")
|
||||
.values(
|
||||
productIds.map((id) => ({
|
||||
sales_channel_id: salesChannelId,
|
||||
product_id: id,
|
||||
}))
|
||||
)
|
||||
.orIgnore()
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ export const SalesChannelServiceMock = {
|
||||
removeProducts: jest.fn().mockImplementation((id, productIds) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
|
||||
addProducts: jest.fn().mockImplementation((id, productIds) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||
import { FindConditions, FindOneOptions } from "typeorm"
|
||||
import { SalesChannel } from "../../models"
|
||||
import { EventBusService, StoreService } from "../index"
|
||||
import SalesChannelService from "../sales-channel"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
import { store, StoreServiceMock } from "../__mocks__/store"
|
||||
import { EventBusService, ProductService, StoreService } from "../index"
|
||||
import { FindConditions, FindOneOptions } from "typeorm"
|
||||
import { SalesChannel } from "../../models"
|
||||
import { ProductServiceMock } from "../__mocks__/product";
|
||||
import { store, StoreServiceMock } from "../__mocks__/store";
|
||||
|
||||
describe("SalesChannelService", () => {
|
||||
const salesChannelData = {
|
||||
@@ -15,7 +16,10 @@ describe("SalesChannelService", () => {
|
||||
|
||||
const salesChannelRepositoryMock = {
|
||||
...MockRepository({
|
||||
findOne: jest.fn().mockImplementation((queryOrId: string | FindOneOptions<SalesChannel>): any => {
|
||||
findOne: jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(queryOrId: string | FindOneOptions<SalesChannel>): any => {
|
||||
return Promise.resolve({
|
||||
id:
|
||||
typeof queryOrId === "string"
|
||||
@@ -40,7 +44,7 @@ describe("SalesChannelService", () => {
|
||||
...salesChannel
|
||||
}),
|
||||
softRemove: jest.fn().mockImplementation((id: string): any => {
|
||||
return Promise.resolve()
|
||||
return Promise.resolve()
|
||||
}),
|
||||
}),
|
||||
getFreeTextSearchResultsAndCount: jest.fn().mockImplementation(() =>
|
||||
@@ -49,9 +53,17 @@ describe("SalesChannelService", () => {
|
||||
id: IdMap.getId("sales_channel_1"),
|
||||
...salesChannelData
|
||||
},
|
||||
]),
|
||||
])
|
||||
),
|
||||
removeProducts: jest.fn().mockImplementation((id: string, productIds: string[]): any => {
|
||||
Promise.resolve([
|
||||
{
|
||||
id: IdMap.getId("sales_channel_1"),
|
||||
...salesChannelData
|
||||
},
|
||||
])
|
||||
}),
|
||||
addProducts: jest.fn().mockImplementation((id: string, productIds: string[]): any => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
}
|
||||
@@ -62,6 +74,7 @@ describe("SalesChannelService", () => {
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -84,6 +97,7 @@ describe("SalesChannelService", () => {
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
storeService: {
|
||||
...StoreServiceMock,
|
||||
retrieve: jest.fn().mockImplementation(() => {
|
||||
@@ -116,6 +130,7 @@ describe("SalesChannelService", () => {
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -146,6 +161,7 @@ describe("SalesChannelService", () => {
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
})
|
||||
|
||||
const update = {
|
||||
@@ -181,6 +197,7 @@ describe("SalesChannelService", () => {
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -244,6 +261,7 @@ describe("SalesChannelService", () => {
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
storeService: {
|
||||
...StoreServiceMock,
|
||||
retrieve: jest.fn().mockImplementation(() => {
|
||||
@@ -299,6 +317,7 @@ describe("SalesChannelService", () => {
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
storeService: StoreServiceMock as unknown as StoreService,
|
||||
})
|
||||
|
||||
@@ -324,4 +343,36 @@ describe("SalesChannelService", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Add products", () => {
|
||||
const salesChannelService = new SalesChannelService({
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService,
|
||||
productService: ProductServiceMock as unknown as ProductService,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should add a list of product to a sales channel', async () => {
|
||||
const salesChannel = await salesChannelService.addProducts(
|
||||
IdMap.getId("sales_channel_1"),
|
||||
[IdMap.getId("sales_channel_1_product_1")]
|
||||
)
|
||||
|
||||
expect(salesChannelRepositoryMock.addProducts).toHaveBeenCalledTimes(1)
|
||||
expect(salesChannelRepositoryMock.addProducts).toHaveBeenCalledWith(
|
||||
IdMap.getId("sales_channel_1"),
|
||||
[IdMap.getId("sales_channel_1_product_1")]
|
||||
)
|
||||
expect(salesChannel).toBeTruthy()
|
||||
expect(salesChannel).toEqual({
|
||||
id: IdMap.getId("sales_channel_1"),
|
||||
...salesChannelData,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,12 +12,15 @@ import {
|
||||
import { buildQuery } from "../utils"
|
||||
import EventBusService from "./event-bus"
|
||||
import StoreService from "./store"
|
||||
import { formatException, PostgresError } from "../utils/exception-formatter"
|
||||
import ProductService from "./product"
|
||||
|
||||
type InjectedDependencies = {
|
||||
salesChannelRepository: typeof SalesChannelRepository
|
||||
eventBusService: EventBusService
|
||||
manager: EntityManager
|
||||
storeService: StoreService
|
||||
productService: ProductService
|
||||
}
|
||||
|
||||
class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
@@ -33,12 +36,14 @@ class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
protected readonly salesChannelRepository_: typeof SalesChannelRepository
|
||||
protected readonly eventBusService_: EventBusService
|
||||
protected readonly storeService_: StoreService
|
||||
protected readonly productService_: ProductService
|
||||
|
||||
constructor({
|
||||
salesChannelRepository,
|
||||
eventBusService,
|
||||
manager,
|
||||
storeService,
|
||||
productService,
|
||||
}: InjectedDependencies) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
@@ -47,6 +52,7 @@ class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
this.salesChannelRepository_ = salesChannelRepository
|
||||
this.eventBusService_ = eventBusService
|
||||
this.storeService_ = storeService
|
||||
this.productService_ = productService
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,6 +271,48 @@ class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
return await this.retrieve(salesChannelId)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a batch of product to a sales channel
|
||||
* @param salesChannelId - The id of the sales channel on which to add the products
|
||||
* @param productIds - The products ids to attach to the sales channel
|
||||
* @return the sales channel on which the products have been added
|
||||
*/
|
||||
async addProducts(
|
||||
salesChannelId: string,
|
||||
productIds: string[]
|
||||
): Promise<SalesChannel | never> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager) => {
|
||||
const salesChannelRepo = transactionManager.getCustomRepository(
|
||||
this.salesChannelRepository_
|
||||
)
|
||||
|
||||
await salesChannelRepo.addProducts(salesChannelId, productIds)
|
||||
|
||||
return await this.retrieve(salesChannelId)
|
||||
},
|
||||
async (error: { code: string }) => {
|
||||
if (error.code === PostgresError.FOREIGN_KEY_ERROR) {
|
||||
const existingProducts = await this.productService_.list({
|
||||
id: productIds,
|
||||
})
|
||||
|
||||
const nonExistingProducts = productIds.filter(
|
||||
(cId) => existingProducts.findIndex((el) => el.id === cId) === -1
|
||||
)
|
||||
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`The following product ids do not exist: ${JSON.stringify(
|
||||
nonExistingProducts.join(", ")
|
||||
)}`
|
||||
)
|
||||
}
|
||||
throw formatException(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SalesChannelService
|
||||
|
||||
Reference in New Issue
Block a user