@@ -2,6 +2,7 @@ const { ModuleRegistrationName } = require("@medusajs/modules-sdk")
|
|||||||
const { medusaIntegrationTestRunner } = require("medusa-test-utils")
|
const { medusaIntegrationTestRunner } = require("medusa-test-utils")
|
||||||
const { createAdminUser } = require("../../../helpers/create-admin-user")
|
const { createAdminUser } = require("../../../helpers/create-admin-user")
|
||||||
const { breaking } = require("../../../helpers/breaking")
|
const { breaking } = require("../../../helpers/breaking")
|
||||||
|
const { ContainerRegistrationKeys } = require("@medusajs/utils")
|
||||||
|
|
||||||
const adminReqConfig = {
|
const adminReqConfig = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -21,6 +22,8 @@ medusaIntegrationTestRunner({
|
|||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
let container
|
let container
|
||||||
let salesChannelService
|
let salesChannelService
|
||||||
|
let productService
|
||||||
|
let remoteQuery
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
;({
|
;({
|
||||||
@@ -41,6 +44,8 @@ medusaIntegrationTestRunner({
|
|||||||
salesChannelService = container.resolve(
|
salesChannelService = container.resolve(
|
||||||
ModuleRegistrationName.SALES_CHANNEL
|
ModuleRegistrationName.SALES_CHANNEL
|
||||||
)
|
)
|
||||||
|
productService = container.resolve(ModuleRegistrationName.PRODUCT)
|
||||||
|
remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("GET /admin/sales-channels/:id", () => {
|
describe("GET /admin/sales-channels/:id", () => {
|
||||||
@@ -687,29 +692,59 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /admin/sales-channels/:id/products/batch", () => {
|
describe("POST /admin/sales-channels/:id/products/batch", () => {
|
||||||
let salesChannel
|
let { salesChannel, product } = {}
|
||||||
let product
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
;({ salesChannel, product } = await breaking(
|
||||||
name: "test name",
|
async () => {
|
||||||
description: "test description",
|
const salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
||||||
})
|
name: "test name",
|
||||||
product = await simpleProductFactory(dbConnection, {
|
description: "test description",
|
||||||
id: "product_1",
|
})
|
||||||
title: "test title",
|
const product = await simpleProductFactory(dbConnection, {
|
||||||
})
|
id: "product_1",
|
||||||
|
title: "test title",
|
||||||
|
})
|
||||||
|
|
||||||
|
return { salesChannel, product }
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const salesChannel = await salesChannelService.create({
|
||||||
|
name: "test name",
|
||||||
|
description: "test description",
|
||||||
|
})
|
||||||
|
const product = await productService.create({
|
||||||
|
title: "test title",
|
||||||
|
})
|
||||||
|
|
||||||
|
return { salesChannel, product }
|
||||||
|
}
|
||||||
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should add products to a sales channel", async () => {
|
it("should add products to a sales channel", async () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
product_ids: [{ id: product.id }],
|
product_ids: breaking(
|
||||||
|
() => [{ id: product.id }],
|
||||||
|
() => [product.id]
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await breaking(
|
||||||
`/admin/sales-channels/${salesChannel.id}/products/batch`,
|
async () => {
|
||||||
payload,
|
return await api.post(
|
||||||
adminReqConfig
|
`/admin/sales-channels/${salesChannel.id}/products/batch`,
|
||||||
|
payload,
|
||||||
|
adminReqConfig
|
||||||
|
)
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await api.post(
|
||||||
|
`/admin/sales-channels/${salesChannel.id}/products/batch/add`,
|
||||||
|
payload,
|
||||||
|
adminReqConfig
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
@@ -725,26 +760,64 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const attachedProduct = await dbConnection.manager.findOne(Product, {
|
const attachedProduct = await breaking(
|
||||||
where: { id: product.id },
|
async () => {
|
||||||
relations: ["sales_channels"],
|
return await dbConnection.manager.findOne(Product, {
|
||||||
})
|
where: { id: product.id },
|
||||||
|
relations: ["sales_channels"],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const [product] = await remoteQuery({
|
||||||
|
products: {
|
||||||
|
fields: ["id"],
|
||||||
|
sales_channels: {
|
||||||
|
fields: ["id", "name", "description", "is_disabled"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return product
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// + default sales channel
|
// + default sales channel
|
||||||
expect(attachedProduct.sales_channels.length).toBe(2)
|
expect(attachedProduct.sales_channels.length).toBe(
|
||||||
|
breaking(
|
||||||
|
() => 2,
|
||||||
|
() => 1 // Comment: The product factory from v1 adds products to the default channel
|
||||||
|
)
|
||||||
|
)
|
||||||
expect(attachedProduct.sales_channels).toEqual(
|
expect(attachedProduct.sales_channels).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining(
|
||||||
expect.objectContaining({
|
breaking(
|
||||||
id: expect.any(String),
|
() => {
|
||||||
name: "test name",
|
return [
|
||||||
description: "test description",
|
expect.objectContaining({
|
||||||
is_disabled: false,
|
id: expect.any(String),
|
||||||
}),
|
name: "test name",
|
||||||
expect.objectContaining({
|
description: "test description",
|
||||||
id: expect.any(String),
|
is_disabled: false,
|
||||||
is_disabled: false,
|
}),
|
||||||
}),
|
expect.objectContaining({
|
||||||
])
|
// Comment: Same as above
|
||||||
|
id: expect.any(String),
|
||||||
|
is_disabled: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
return [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: "test name",
|
||||||
|
description: "test description",
|
||||||
|
is_disabled: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
links: {
|
||||||
|
sales_channel_id: string
|
||||||
|
product_ids: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const associateProductsWithSalesChannelsStepId =
|
||||||
|
"associate-products-with-channels"
|
||||||
|
export const associateProductsWithSalesChannelsStep = createStep(
|
||||||
|
associateProductsWithSalesChannelsStepId,
|
||||||
|
async (input: StepInput, { container }) => {
|
||||||
|
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
|
||||||
|
const links = input.links
|
||||||
|
.map((link) => {
|
||||||
|
return link.product_ids.map((id) => {
|
||||||
|
return {
|
||||||
|
[Modules.PRODUCT]: {
|
||||||
|
product_id: id,
|
||||||
|
},
|
||||||
|
[Modules.SALES_CHANNEL]: {
|
||||||
|
sales_channel_id: link.sales_channel_id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const createdLinks = await remoteLink.create(links)
|
||||||
|
|
||||||
|
return new StepResponse(createdLinks, links)
|
||||||
|
},
|
||||||
|
async (links, { container }) => {
|
||||||
|
if (!links) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
|
||||||
|
await remoteLink.dismiss(links)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
export * from "./associate-products-with-channels"
|
||||||
export * from "./create-sales-channels"
|
export * from "./create-sales-channels"
|
||||||
export * from "./update-sales-channels"
|
|
||||||
export * from "./delete-sales-channels"
|
export * from "./delete-sales-channels"
|
||||||
|
export * from "./update-sales-channels"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { SalesChannelDTO } from "@medusajs/types"
|
||||||
|
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||||
|
import { associateProductsWithSalesChannelsStep } from "../steps/associate-products-with-channels"
|
||||||
|
|
||||||
|
type WorkflowInput = {
|
||||||
|
data: {
|
||||||
|
sales_channel_id: string
|
||||||
|
product_ids: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addProductsToSalesChannelsWorkflowId =
|
||||||
|
"add-products-to-sales-channels"
|
||||||
|
export const addProductsToSalesChannelsWorkflow = createWorkflow(
|
||||||
|
addProductsToSalesChannelsWorkflowId,
|
||||||
|
(input: WorkflowData<WorkflowInput>): WorkflowData<SalesChannelDTO[]> => {
|
||||||
|
return associateProductsWithSalesChannelsStep({ links: input.data })
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
export * from "./add-products-to-sales-channels"
|
||||||
export * from "./create-sales-channels"
|
export * from "./create-sales-channels"
|
||||||
export * from "./update-sales-channels"
|
|
||||||
export * from "./delete-sales-channels"
|
export * from "./delete-sales-channels"
|
||||||
|
export * from "./update-sales-channels"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { addProductsToSalesChannelsWorkflow } from "@medusajs/core-flows"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../../../../../../../types/routing"
|
||||||
|
import { defaultAdminSalesChannelFields } from "../../../../query-config"
|
||||||
|
import { AdminPostSalesChannelsChannelProductsBatchReq } from "../../../../validators"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => {
|
||||||
|
const body =
|
||||||
|
req.validatedBody as AdminPostSalesChannelsChannelProductsBatchReq
|
||||||
|
|
||||||
|
const workflowInput = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
sales_channel_id: req.params.id,
|
||||||
|
product_ids: body.product_ids,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const { errors } = await addProductsToSalesChannelsWorkflow(req.scope).run({
|
||||||
|
input: workflowInput,
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
throw errors[0].error
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "sales_channels",
|
||||||
|
variables: { id: req.params.id },
|
||||||
|
fields: defaultAdminSalesChannelFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [sales_channel] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ sales_channel })
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import * as QueryConfig from "./query-config"
|
|||||||
import {
|
import {
|
||||||
AdminGetSalesChannelsParams,
|
AdminGetSalesChannelsParams,
|
||||||
AdminGetSalesChannelsSalesChannelParams,
|
AdminGetSalesChannelsSalesChannelParams,
|
||||||
|
AdminPostSalesChannelsChannelProductsBatchReq,
|
||||||
AdminPostSalesChannelsReq,
|
AdminPostSalesChannelsReq,
|
||||||
AdminPostSalesChannelsSalesChannelReq,
|
AdminPostSalesChannelsSalesChannelReq,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
@@ -56,4 +57,9 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
matcher: "/admin/sales-channels/:id",
|
matcher: "/admin/sales-channels/:id",
|
||||||
middlewares: [],
|
middlewares: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/sales-channels/:id/products/batch/add",
|
||||||
|
middlewares: [transformBody(AdminPostSalesChannelsChannelProductsBatchReq)],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { OperatorMap } from "@medusajs/types"
|
import { OperatorMap } from "@medusajs/types"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import {
|
import {
|
||||||
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
@@ -100,3 +101,8 @@ export class AdminPostSalesChannelsSalesChannelReq {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AdminPostSalesChannelsChannelProductsBatchReq {
|
||||||
|
@IsArray()
|
||||||
|
product_ids: string[]
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user