From edafe7db4780631601d07e43b18f80c3406166f0 Mon Sep 17 00:00:00 2001 From: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> Date: Wed, 3 Apr 2024 08:38:53 +0200 Subject: [PATCH] Feat(link-module, core-flows, medusa): Add sales channel location management (#6905) * add locations with sales channels * init, not working location based management * working adding sales channel stock locations * remote sales channels from location * remove console log * rm allowedFields * redo errorhandler * make validation take an array * add changeset * fix build * Update packages/core-flows/src/sales-channel/workflows/remove-locations-from-sales-channel.ts Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> * add default cases to return early * cleanup * add sc test and remove associations --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> --- .changeset/shaggy-bobcats-camp.md | 7 ++ .../api/__tests__/admin/sales-channels.js | 49 +++++++++- .../admin/stock-location/index.spec.ts | 95 +++++++++++++++++++ .../steps/associate-locations-with-channel.ts | 52 ++++++++++ .../src/sales-channel/steps/index.ts | 3 +- .../steps/remove-locations-from-channels.ts | 71 ++++++++++++++ .../add-locations-to-sales-channel.ts | 20 ++++ .../workflows/delete-sales-channels.ts | 9 +- .../src/sales-channel/workflows/index.ts | 3 +- .../remove-locations-from-sales-channel.ts | 19 ++++ .../src/services/link-module-service.ts | 36 ++++--- packages/link-modules/src/services/link.ts | 36 +++++-- .../[id]/rules/batch/remove/route.ts | 5 +- .../api-v2/admin/sales-channels/[id]/route.ts | 2 +- .../[id]/sales-channels/batch/add/route.ts | 44 +++++++++ .../[id]/sales-channels/batch/remove/route.ts | 46 +++++++++ .../admin/stock-locations/middlewares.ts | 12 +++ .../admin/stock-locations/query-config.ts | 1 - .../admin/stock-locations/validators.ts | 16 ++-- 19 files changed, 483 insertions(+), 43 deletions(-) create mode 100644 .changeset/shaggy-bobcats-camp.md create mode 100644 packages/core-flows/src/sales-channel/steps/associate-locations-with-channel.ts create mode 100644 packages/core-flows/src/sales-channel/steps/remove-locations-from-channels.ts create mode 100644 packages/core-flows/src/sales-channel/workflows/add-locations-to-sales-channel.ts create mode 100644 packages/core-flows/src/sales-channel/workflows/remove-locations-from-sales-channel.ts create mode 100644 packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/add/route.ts create mode 100644 packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/remove/route.ts diff --git a/.changeset/shaggy-bobcats-camp.md b/.changeset/shaggy-bobcats-camp.md new file mode 100644 index 0000000000..c0c02f02c1 --- /dev/null +++ b/.changeset/shaggy-bobcats-camp.md @@ -0,0 +1,7 @@ +--- +"@medusajs/link-modules": patch +"@medusajs/core-flows": patch +"@medusajs/medusa": patch +--- + +feat(link-modules, core-flows, medusa): add sales channel location management endpoint diff --git a/integration-tests/api/__tests__/admin/sales-channels.js b/integration-tests/api/__tests__/admin/sales-channels.js index 64cfc745bd..1d12439d79 100644 --- a/integration-tests/api/__tests__/admin/sales-channels.js +++ b/integration-tests/api/__tests__/admin/sales-channels.js @@ -1,6 +1,9 @@ const { ModuleRegistrationName, Modules } = require("@medusajs/modules-sdk") const { medusaIntegrationTestRunner } = require("medusa-test-utils") -const { createAdminUser } = require("../../../helpers/create-admin-user") +const { + createAdminUser, + adminHeaders, +} = require("../../../helpers/create-admin-user") const { breaking } = require("../../../helpers/breaking") const { ContainerRegistrationKeys } = require("@medusajs/utils") @@ -345,7 +348,7 @@ medusaIntegrationTestRunner({ }) it("should delete the requested sales channel", async () => { - let toDelete = await breaking( + const toDelete = await breaking( async () => { return await dbConnection.manager.findOne(SalesChannel, { where: { id: salesChannel.id }, @@ -403,6 +406,48 @@ medusaIntegrationTestRunner({ ) }) }) + + it("should successfully delete channel associations", async () => { + await breaking(null, async () => { + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + console.warn("testing") + await remoteLink.create([ + { + [Modules.SALES_CHANNEL]: { + sales_channel_id: "test-channel", + }, + [Modules.STOCK_LOCATION]: { + stock_location_id: "test-location", + }, + }, + { + [Modules.SALES_CHANNEL]: { + sales_channel_id: "test-channel-default", + }, + [Modules.STOCK_LOCATION]: { + stock_location_id: "test-location", + }, + }, + ]) + + await api + .delete(`/admin/sales-channels/test-channel`, adminReqConfig) + .catch(console.log) + + const linkService = remoteLink.getLinkModule( + Modules.SALES_CHANNEL, + "sales_channel_id", + Modules.STOCK_LOCATION, + "stock_location_id" + ) + + const channelLinks = await linkService.list() + expect(channelLinks).toHaveLength(1) + }) + }) }) describe("GET /admin/orders/:id", () => { diff --git a/integration-tests/api/__tests__/admin/stock-location/index.spec.ts b/integration-tests/api/__tests__/admin/stock-location/index.spec.ts index bdf04adb5d..a0fccaf034 100644 --- a/integration-tests/api/__tests__/admin/stock-location/index.spec.ts +++ b/integration-tests/api/__tests__/admin/stock-location/index.spec.ts @@ -279,5 +279,100 @@ medusaIntegrationTestRunner({ expect(stockLocationLinks).toHaveLength(0) }) }) + + describe("Add sales channels", () => { + let salesChannel + let location + + beforeEach(async () => { + const salesChannelResponse = await api.post( + "/admin/sales-channels", + { + name: "test name", + description: "test description", + }, + adminHeaders + ) + salesChannel = salesChannelResponse.data.sales_channel + + const locationResponse = await api.post( + "/admin/stock-locations", + { + name: "test location", + }, + adminHeaders + ) + + location = locationResponse.data.stock_location + }) + + it("should add sales channels to a location", async () => { + const salesChannelResponse = await api.post( + `/admin/stock-locations/${location.id}/sales-channels/batch/add?fields=*sales_channels`, + { sales_channel_ids: [salesChannel.id] }, + adminHeaders + ) + + expect( + salesChannelResponse.data.stock_location.sales_channels + ).toHaveLength(1) + }) + }) + + describe("Remove sales channels", () => { + let salesChannel1 + let salesChannel2 + let location + + beforeEach(async () => { + const salesChannelResponse1 = await api.post( + "/admin/sales-channels", + { + name: "test name", + description: "test description", + }, + adminHeaders + ) + salesChannel1 = salesChannelResponse1.data.sales_channel + + const salesChannelResponse2 = await api.post( + "/admin/sales-channels", + { + name: "test name", + description: "test description", + }, + adminHeaders + ) + salesChannel2 = salesChannelResponse2.data.sales_channel + + const locationResponse = await api.post( + "/admin/stock-locations", + { + name: "test location", + }, + adminHeaders + ) + + location = locationResponse.data.stock_location + + await api.post( + `/admin/stock-locations/${location.id}/sales-channels/batch/add?fields=*sales_channels`, + { sales_channel_ids: [salesChannel1.id, salesChannel2.id] }, + adminHeaders + ) + }) + + it("should remove sales channels from a location", async () => { + const salesChannelResponse = await api.post( + `/admin/stock-locations/${location.id}/sales-channels/batch/remove?fields=*sales_channels`, + { sales_channel_ids: [salesChannel1.id] }, + adminHeaders + ) + + expect( + salesChannelResponse.data.stock_location.sales_channels + ).toHaveLength(1) + }) + }) }, }) diff --git a/packages/core-flows/src/sales-channel/steps/associate-locations-with-channel.ts b/packages/core-flows/src/sales-channel/steps/associate-locations-with-channel.ts new file mode 100644 index 0000000000..1c8e8937bc --- /dev/null +++ b/packages/core-flows/src/sales-channel/steps/associate-locations-with-channel.ts @@ -0,0 +1,52 @@ +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +import { ContainerRegistrationKeys } from "@medusajs/utils" +import { Modules } from "@medusajs/modules-sdk" + +interface StepInput { + links: { + sales_channel_id: string + location_ids: string[] + }[] +} + +export const associateLocationsWithChannelStepId = + "associate-locations-with-channel-step" +export const associateLocationsWithChannelStep = createStep( + associateLocationsWithChannelStepId, + async (data: StepInput, { container }) => { + if (!data.links.length) { + return new StepResponse([], []) + } + + const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + + const links = data.links + .map((link) => { + return link.location_ids.map((id) => { + return { + [Modules.SALES_CHANNEL]: { + sales_channel_id: link.sales_channel_id, + }, + [Modules.STOCK_LOCATION]: { + stock_location_id: id, + }, + } + }) + }) + .flat() + + const createdLinks = await remoteLink.create(links) + + return new StepResponse(createdLinks, links) + }, + async (links, { container }) => { + if (!links?.length) { + return + } + + const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + + await remoteLink.dismiss(links) + } +) diff --git a/packages/core-flows/src/sales-channel/steps/index.ts b/packages/core-flows/src/sales-channel/steps/index.ts index 8380f657ce..2ab23e59fc 100644 --- a/packages/core-flows/src/sales-channel/steps/index.ts +++ b/packages/core-flows/src/sales-channel/steps/index.ts @@ -4,4 +4,5 @@ export * from "./create-sales-channels" export * from "./delete-sales-channels" export * from "./detach-products-from-sales-channels" export * from "./update-sales-channels" - +export * from "./associate-locations-with-channel" +export * from "./remove-locations-from-channels" diff --git a/packages/core-flows/src/sales-channel/steps/remove-locations-from-channels.ts b/packages/core-flows/src/sales-channel/steps/remove-locations-from-channels.ts new file mode 100644 index 0000000000..43b38d48ec --- /dev/null +++ b/packages/core-flows/src/sales-channel/steps/remove-locations-from-channels.ts @@ -0,0 +1,71 @@ +import { Modules, RemoteLink } from "@medusajs/modules-sdk" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +import { ContainerRegistrationKeys } from "@medusajs/utils" + +export const removeLocationsFromSalesChannelStepId = + "remove-locations-from-sales-channel" +export const removeLocationsFromSalesChannelStep = createStep( + removeLocationsFromSalesChannelStepId, + async ( + data: { + sales_channel_id: string + location_ids: string[] + }[], + { container } + ) => { + if (!data.length) { + return new StepResponse([], []) + } + + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + const linkModule = remoteLink.getLinkModule( + Modules.SALES_CHANNEL, + "sales_channel_id", + Modules.STOCK_LOCATION, + "stock_location_id" + ) + + if (!linkModule) { + return new StepResponse([], []) + } + + const links = data + .map((d) => + d.location_ids.map((locId) => ({ + sales_channel_id: d.sales_channel_id, + stock_location_id: locId, + })) + ) + .flat() + + await linkModule.softDelete(links) + + return new StepResponse(void 0, links) + }, + async (links, { container }) => { + if (!links?.length) { + return + } + + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + const linkModule = remoteLink.getLinkModule( + Modules.SALES_CHANNEL, + "sales_channel_id", + Modules.STOCK_LOCATION, + "stock_location_id" + ) + + if (!linkModule) { + return + } + + await linkModule.restore(links) + } +) diff --git a/packages/core-flows/src/sales-channel/workflows/add-locations-to-sales-channel.ts b/packages/core-flows/src/sales-channel/workflows/add-locations-to-sales-channel.ts new file mode 100644 index 0000000000..649a935828 --- /dev/null +++ b/packages/core-flows/src/sales-channel/workflows/add-locations-to-sales-channel.ts @@ -0,0 +1,20 @@ +import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" + +import { SalesChannelDTO } from "@medusajs/types" +import { associateLocationsWithChannelStep } from "../steps" + +interface WorkflowInput { + data: { + sales_channel_id: string + location_ids: string[] + }[] +} + +export const addLocationsToSalesChannelWorkflowId = + "add-locations-to-sales-channel" +export const addLocationsToSalesChannelWorkflow = createWorkflow( + addLocationsToSalesChannelWorkflowId, + (input: WorkflowData): WorkflowData => { + return associateLocationsWithChannelStep({ links: input.data }) + } +) diff --git a/packages/core-flows/src/sales-channel/workflows/delete-sales-channels.ts b/packages/core-flows/src/sales-channel/workflows/delete-sales-channels.ts index dbbffa1675..f1d158d764 100644 --- a/packages/core-flows/src/sales-channel/workflows/delete-sales-channels.ts +++ b/packages/core-flows/src/sales-channel/workflows/delete-sales-channels.ts @@ -1,5 +1,8 @@ import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" + +import { Modules } from "@medusajs/modules-sdk" import { deleteSalesChannelsStep } from "../steps/delete-sales-channels" +import { removeRemoteLinkStep } from "../../common/steps/remove-remote-links" type WorkflowInput = { ids: string[] } @@ -7,6 +10,10 @@ export const deleteSalesChannelsWorkflowId = "delete-sales-channels" export const deleteSalesChannelsWorkflow = createWorkflow( deleteSalesChannelsWorkflowId, (input: WorkflowData): WorkflowData => { - return deleteSalesChannelsStep(input.ids) + deleteSalesChannelsStep(input.ids) + + removeRemoteLinkStep({ + [Modules.SALES_CHANNEL]: { sales_channel_id: input.ids }, + }) } ) diff --git a/packages/core-flows/src/sales-channel/workflows/index.ts b/packages/core-flows/src/sales-channel/workflows/index.ts index a12ea37c1a..1194cbc358 100644 --- a/packages/core-flows/src/sales-channel/workflows/index.ts +++ b/packages/core-flows/src/sales-channel/workflows/index.ts @@ -3,4 +3,5 @@ export * from "./create-sales-channels" export * from "./delete-sales-channels" export * from "./remove-products-from-sales-channels" export * from "./update-sales-channels" - +export * from "./add-locations-to-sales-channel" +export * from "./remove-locations-from-sales-channel" diff --git a/packages/core-flows/src/sales-channel/workflows/remove-locations-from-sales-channel.ts b/packages/core-flows/src/sales-channel/workflows/remove-locations-from-sales-channel.ts new file mode 100644 index 0000000000..5235cc14c7 --- /dev/null +++ b/packages/core-flows/src/sales-channel/workflows/remove-locations-from-sales-channel.ts @@ -0,0 +1,19 @@ +import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" + +import { removeLocationsFromSalesChannelStep } from "../steps" + +interface WorkflowInput { + data: { + sales_channel_id: string + location_ids: string[] + }[] +} + +export const removeLocationsFromSalesChannelWorkflowId = + "remove-locations-from-sales-channel" +export const removeLocationsFromSalesChannelWorkflow = createWorkflow( + removeLocationsFromSalesChannelWorkflowId, + (input: WorkflowData): WorkflowData => { + removeLocationsFromSalesChannelStep(input.data) + } +) diff --git a/packages/link-modules/src/services/link-module-service.ts b/packages/link-modules/src/services/link-module-service.ts index 454c3ce921..7a649f76ba 100644 --- a/packages/link-modules/src/services/link-module-service.ts +++ b/packages/link-modules/src/services/link-module-service.ts @@ -100,16 +100,19 @@ export default class LinkModuleService implements ILinkModule { return this.primaryKey_.concat(this.foreignKey_).includes(name) } - private validateFields(data: any) { - const keys = Object.keys(data) - if (!keys.every((k) => this.isValidKeyName(k))) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Invalid field name provided. Valid field names are ${this.primaryKey_.concat( - this.foreignKey_ - )}` - ) - } + private validateFields(data: any | any[]) { + const dataToValidate = Array.isArray(data) ? data : [data] + dataToValidate.forEach((d) => { + const keys = Object.keys(d) + if (keys.some((k) => !this.isValidKeyName(k))) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Invalid field name provided. Valid field names are ${this.primaryKey_.concat( + this.foreignKey_ + )}` + ) + } + }) } @InjectManager("baseRepository_") @@ -276,10 +279,12 @@ export default class LinkModuleService implements ILinkModule { { returnLinkableKeys }: SoftDeleteReturn = {}, @MedusaContext() sharedContext: Context = {} ): Promise | void> { - this.validateFields(data) + const inputArray = Array.isArray(data) ? data : [data] + + this.validateFields(inputArray) let [deletedEntities, cascadedEntitiesMap] = await this.softDelete_( - data, + inputArray, sharedContext ) @@ -324,7 +329,7 @@ export default class LinkModuleService implements ILinkModule { @InjectTransactionManager(shouldForceTransaction, "baseRepository_") protected async softDelete_( - data: any, + data: any[], @MedusaContext() sharedContext: Context = {} ): Promise<[object[], Record]> { return await this.linkService_.softDelete(data, sharedContext) @@ -335,10 +340,11 @@ export default class LinkModuleService implements ILinkModule { { returnLinkableKeys }: RestoreReturn = {}, @MedusaContext() sharedContext: Context = {} ): Promise | void> { - this.validateFields(data) + const inputArray = Array.isArray(data) ? data : [data] + this.validateFields(inputArray) let [restoredEntities, cascadedEntitiesMap] = await this.restore_( - data, + inputArray, sharedContext ) diff --git a/packages/link-modules/src/services/link.ts b/packages/link-modules/src/services/link.ts index 42a69b0840..2b6094e903 100644 --- a/packages/link-modules/src/services/link.ts +++ b/packages/link-modules/src/services/link.ts @@ -87,15 +87,24 @@ export default class LinkService { @InjectTransactionManager(doNotForceTransaction, "linkRepository_") async softDelete( - data: any, + data: any[], @MedusaContext() sharedContext: Context = {} ): Promise<[object[], Record]> { - const filter = {} - for (const key in data) { - filter[key] = { $in: Array.isArray(data[key]) ? data[key] : [data[key]] } + const deleteFilters = { + $or: data.map((dataEntry) => { + const filter = {} + for (const key in dataEntry) { + filter[key] = { + $in: Array.isArray(dataEntry[key]) + ? dataEntry[key] + : [dataEntry[key]], + } + } + return filter + }), } - return await this.linkRepository_.softDelete(filter, { + return await this.linkRepository_.softDelete(deleteFilters, { transactionManager: sharedContext.transactionManager, }) } @@ -105,12 +114,21 @@ export default class LinkService { data: any, @MedusaContext() sharedContext: Context = {} ): Promise<[object[], Record]> { - const filter = {} - for (const key in data) { - filter[key] = { $in: Array.isArray(data[key]) ? data[key] : [data[key]] } + const restoreFilters = { + $or: data.map((dataEntry) => { + const filter = {} + for (const key in dataEntry) { + filter[key] = { + $in: Array.isArray(dataEntry[key]) + ? dataEntry[key] + : [dataEntry[key]], + } + } + return filter + }), } - return await this.linkRepository_.restore(data, { + return await this.linkRepository_.restore(restoreFilters, { transactionManager: sharedContext.transactionManager, }) } diff --git a/packages/medusa/src/api-v2/admin/promotions/[id]/rules/batch/remove/route.ts b/packages/medusa/src/api-v2/admin/promotions/[id]/rules/batch/remove/route.ts index 767b2565b9..c5543fee72 100644 --- a/packages/medusa/src/api-v2/admin/promotions/[id]/rules/batch/remove/route.ts +++ b/packages/medusa/src/api-v2/admin/promotions/[id]/rules/batch/remove/route.ts @@ -1,10 +1,11 @@ -import { removeRulesFromPromotionsWorkflow } from "@medusajs/core-flows" -import { RuleType } from "@medusajs/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../../../../types/routing" + import { AdminPostPromotionsPromotionRulesBatchRemoveReq } from "../../../../validators" +import { RuleType } from "@medusajs/utils" +import { removeRulesFromPromotionsWorkflow } from "@medusajs/core-flows" export const POST = async ( req: AuthenticatedMedusaRequest, diff --git a/packages/medusa/src/api-v2/admin/sales-channels/[id]/route.ts b/packages/medusa/src/api-v2/admin/sales-channels/[id]/route.ts index 7eb775dbe1..5b9a5b7b13 100644 --- a/packages/medusa/src/api-v2/admin/sales-channels/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/sales-channels/[id]/route.ts @@ -25,7 +25,7 @@ export const GET = async ( const queryObject = remoteQueryObjectFromString({ entryPoint: "sales_channels", variables, - fields: defaultAdminSalesChannelFields, + fields: req.remoteQueryConfig.fields, }) const [sales_channel] = await remoteQuery(queryObject) diff --git a/packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/add/route.ts b/packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/add/route.ts new file mode 100644 index 0000000000..61bbb5dbae --- /dev/null +++ b/packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/add/route.ts @@ -0,0 +1,44 @@ +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { + MedusaRequest, + MedusaResponse, +} from "../../../../../../../types/routing" + +import { AdminStockLocationsLocationSalesChannelBatchReq } from "../../../../validators" +import { addLocationsToSalesChannelWorkflow } from "@medusajs/core-flows" + +export const POST = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const workflowInput = { + data: req.validatedBody.sales_channel_ids.map((id) => ({ + sales_channel_id: id, + location_ids: [req.params.id], + })), + } + + const { errors } = await addLocationsToSalesChannelWorkflow(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: "stock_locations", + variables: { id: req.params.id }, + fields: req.remoteQueryConfig.fields, + }) + + const [stock_location] = await remoteQuery(queryObject) + + res.status(200).json({ stock_location }) +} diff --git a/packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/remove/route.ts b/packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/remove/route.ts new file mode 100644 index 0000000000..1c192cfec7 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/stock-locations/[id]/sales-channels/batch/remove/route.ts @@ -0,0 +1,46 @@ +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { + MedusaRequest, + MedusaResponse, +} from "../../../../../../../types/routing" + +import { AdminStockLocationsLocationSalesChannelBatchReq } from "../../../../validators" +import { removeLocationsFromSalesChannelWorkflow } from "@medusajs/core-flows" + +export const POST = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const workflowInput = { + data: req.validatedBody.sales_channel_ids.map((id) => ({ + sales_channel_id: id, + location_ids: [req.params.id], + })), + } + + const { errors } = await removeLocationsFromSalesChannelWorkflow( + 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: "stock_locations", + variables: { id: req.params.id }, + fields: req.remoteQueryConfig.fields, + }) + + const [stock_location] = await remoteQuery(queryObject) + + res.status(200).json({ stock_location }) +} diff --git a/packages/medusa/src/api-v2/admin/stock-locations/middlewares.ts b/packages/medusa/src/api-v2/admin/stock-locations/middlewares.ts index f2f5c90f56..382342caed 100644 --- a/packages/medusa/src/api-v2/admin/stock-locations/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/stock-locations/middlewares.ts @@ -7,6 +7,7 @@ import { AdminPostStockLocationsLocationReq, AdminPostStockLocationsParams, AdminPostStockLocationsReq, + AdminStockLocationsLocationSalesChannelBatchReq, } from "./validators" import { transformBody, transformQuery } from "../../../api/middlewares" @@ -53,6 +54,17 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["POST"], + matcher: "/admin/stock-locations/:id/sales-channels/batch*", + middlewares: [ + transformBody(AdminStockLocationsLocationSalesChannelBatchReq), + transformQuery( + AdminPostStockLocationsLocationParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, { method: ["GET"], matcher: "/admin/stock-locations/:id", diff --git a/packages/medusa/src/api-v2/admin/stock-locations/query-config.ts b/packages/medusa/src/api-v2/admin/stock-locations/query-config.ts index 227e332fc5..a2dad4710e 100644 --- a/packages/medusa/src/api-v2/admin/stock-locations/query-config.ts +++ b/packages/medusa/src/api-v2/admin/stock-locations/query-config.ts @@ -17,7 +17,6 @@ export const defaultAdminStockLocationFields = [ export const retrieveTransformQueryConfig = { defaults: defaultAdminStockLocationFields, - allowed: defaultAdminStockLocationFields, isList: false, } diff --git a/packages/medusa/src/api-v2/admin/stock-locations/validators.ts b/packages/medusa/src/api-v2/admin/stock-locations/validators.ts index 277e89be20..9589f9a36a 100644 --- a/packages/medusa/src/api-v2/admin/stock-locations/validators.ts +++ b/packages/medusa/src/api-v2/admin/stock-locations/validators.ts @@ -1,15 +1,6 @@ +import { FindParams, extendedFindParamsMixin } from "../../../types/common" import { - DateComparisonOperator, - FindParams, - NumericalComparisonOperator, - StringComparisonOperator, - extendedFindParamsMixin, -} from "../../../types/common" -import { - IsBoolean, - IsEmail, IsNotEmpty, - IsNumber, IsObject, IsOptional, IsString, @@ -290,3 +281,8 @@ export class AdminPostStockLocationsLocationReq { export class AdminPostStockLocationsLocationParams extends FindParams {} export class AdminGetStockLocationsLocationParams extends FindParams {} + +export class AdminStockLocationsLocationSalesChannelBatchReq { + @IsString({ each: true }) + sales_channel_ids: string[] +}