fix(medusa): sales_channel_id middleware manipulation (#12234)
* fix(medusa): sales_channel_id middleware manipulation leading to lost of the sc * fix(medusa): sales_channel_id middleware manipulation leading to lost of the sc * add unit tests * add unit tests * improve * integration tests
This commit is contained in:
1 parent
a8a7af46a6
commit
49c526399e
5 files changed
+494
-49
No files matched your search
@@ -1478,7 +1478,7 @@ medusaIntegrationTestRunner({
|
|||||||
describe("with inventory items", () => {
|
describe("with inventory items", () => {
|
||||||
let location1
|
let location1
|
||||||
let location2
|
let location2
|
||||||
let salesChannel1
|
let salesChannel1, salesChannel2
|
||||||
let publishableKey1
|
let publishableKey1
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -1503,6 +1503,11 @@ medusaIntegrationTestRunner({
|
|||||||
[product.id, product2.id]
|
[product.id, product2.id]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
salesChannel2 = await createSalesChannel(
|
||||||
|
{ name: "sales channel test 2" },
|
||||||
|
[product.id, product2.id]
|
||||||
|
)
|
||||||
|
|
||||||
const api1Res = await api.post(
|
const api1Res = await api.post(
|
||||||
`/admin/api-keys`,
|
`/admin/api-keys`,
|
||||||
{ title: "Test publishable KEY", type: ApiKeyType.PUBLISHABLE },
|
{ title: "Test publishable KEY", type: ApiKeyType.PUBLISHABLE },
|
||||||
@@ -1721,6 +1726,63 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should list all inventory items for a variant in a given sales channel passed as a query param AND when there are multiple sales channels associated with the publishable key", async () => {
|
||||||
|
await api.post(
|
||||||
|
`/admin/api-keys/${publishableKey1.id}/sales-channels`,
|
||||||
|
{ add: [salesChannel2.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/store/products?sales_channel_id[]=${salesChannel1.id}&fields=variants.inventory_items.inventory.location_levels.*`,
|
||||||
|
{ headers: { "x-publishable-api-key": publishableKey1.token } }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(2)
|
||||||
|
expect(response.data.products).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: product.id,
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
inventory_items: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
inventory_item_id: inventoryItem1.id,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
inventory_item_id: inventoryItem2.id,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw when multiple sales channels are passed as a query param AND there are multiple sales channels associated with the publishable key", async () => {
|
||||||
|
await api.post(
|
||||||
|
`/admin/api-keys/${publishableKey1.id}/sales-channels`,
|
||||||
|
{ add: [salesChannel2.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
let error = await api
|
||||||
|
.get(
|
||||||
|
`/store/products?sales_channel_id[]=${salesChannel1.id}&sales_channel_id[]=${salesChannel2.id}&fields=variants.inventory_quantity`,
|
||||||
|
{ headers: { "x-publishable-api-key": publishableKey1.token } }
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.response.status).toEqual(400)
|
||||||
|
expect(error.response.data).toEqual({
|
||||||
|
message:
|
||||||
|
"Inventory availability cannot be calculated in the given context. Either provide a single sales channel id or configure a single sales channel in the publishable key",
|
||||||
|
type: "invalid_data",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("should return inventory quantity when variant's manage_inventory is true", async () => {
|
it("should return inventory quantity when variant's manage_inventory is true", async () => {
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/products/${product.id}/variants/${variant.id}/inventory-items`,
|
`/admin/products/${product.id}/variants/${variant.id}/inventory-items`,
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
import { MedusaStoreRequest } from "@medusajs/framework/http"
|
||||||
|
import { MedusaError } from "@medusajs/framework/utils"
|
||||||
|
import { NextFunction } from "express"
|
||||||
|
import {
|
||||||
|
transformAndValidateSalesChannelIds,
|
||||||
|
filterByValidSalesChannels,
|
||||||
|
} from "../filter-by-valid-sales-channels"
|
||||||
|
|
||||||
|
describe("filter-by-valid-sales-channels", () => {
|
||||||
|
describe("transformAndValidateSalesChannelIds", () => {
|
||||||
|
let req: Partial<MedusaStoreRequest>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
publishable_key_context: {
|
||||||
|
key: "test-key",
|
||||||
|
sales_channel_ids: ["sc-1", "sc-2"],
|
||||||
|
},
|
||||||
|
validatedQuery: {},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return sales channel ids from request when they exist and are in publishable key", () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: ["sc-1"] }
|
||||||
|
|
||||||
|
const result = transformAndValidateSalesChannelIds(
|
||||||
|
req as MedusaStoreRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual(["sc-1"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle sales_channel_id as string and transform to array", () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: "sc-2" }
|
||||||
|
|
||||||
|
const result = transformAndValidateSalesChannelIds(
|
||||||
|
req as MedusaStoreRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual(["sc-2"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw error when requested sales channel is not in publishable key", () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: ["sc-3"] }
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
transformAndValidateSalesChannelIds(req as MedusaStoreRequest)
|
||||||
|
}).toThrow(MedusaError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return sales channel ids from publishable key when no ids in request", () => {
|
||||||
|
req.validatedQuery = {}
|
||||||
|
|
||||||
|
const result = transformAndValidateSalesChannelIds(
|
||||||
|
req as MedusaStoreRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual(["sc-1", "sc-2"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return empty array when no sales channel ids in publishable key or request", () => {
|
||||||
|
req.publishable_key_context = {
|
||||||
|
key: "test-key",
|
||||||
|
sales_channel_ids: [],
|
||||||
|
}
|
||||||
|
req.validatedQuery = {}
|
||||||
|
|
||||||
|
const result = transformAndValidateSalesChannelIds(
|
||||||
|
req as MedusaStoreRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("filterByValidSalesChannels", () => {
|
||||||
|
let req: Partial<MedusaStoreRequest>
|
||||||
|
let res: any
|
||||||
|
let next: NextFunction
|
||||||
|
let middleware: ReturnType<typeof filterByValidSalesChannels>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
publishable_key_context: {
|
||||||
|
key: "test-key",
|
||||||
|
sales_channel_ids: ["sc-1", "sc-2"],
|
||||||
|
},
|
||||||
|
validatedQuery: {},
|
||||||
|
filterableFields: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
res = {}
|
||||||
|
next = jest.fn()
|
||||||
|
middleware = filterByValidSalesChannels()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should set filterableFields.sales_channel_id and call next", async () => {
|
||||||
|
await middleware(req as MedusaStoreRequest, res, next)
|
||||||
|
|
||||||
|
expect(req.filterableFields!.sales_channel_id).toEqual(["sc-1", "sc-2"])
|
||||||
|
expect(next).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw error when no sales channels available", async () => {
|
||||||
|
req.publishable_key_context = {
|
||||||
|
key: "test-key",
|
||||||
|
sales_channel_ids: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
middleware(req as MedusaStoreRequest, res, next)
|
||||||
|
).rejects.toThrow(
|
||||||
|
"Publishable key needs to have a sales channel configured"
|
||||||
|
)
|
||||||
|
expect(next).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should use only sales channels from request that are in publishable key", async () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: ["sc-1"] }
|
||||||
|
|
||||||
|
await middleware(req as MedusaStoreRequest, res, next)
|
||||||
|
|
||||||
|
expect(req.filterableFields!.sales_channel_id).toEqual(["sc-1"])
|
||||||
|
expect(next).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle sales_channel_id as string in request", async () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: "sc-2" }
|
||||||
|
|
||||||
|
await middleware(req as MedusaStoreRequest, res, next)
|
||||||
|
|
||||||
|
expect(req.filterableFields!.sales_channel_id).toEqual(["sc-2"])
|
||||||
|
expect(next).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
+233
@@ -0,0 +1,233 @@
|
|||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
deepCopy,
|
||||||
|
getTotalVariantAvailability,
|
||||||
|
getVariantAvailability,
|
||||||
|
MedusaError,
|
||||||
|
} from "@medusajs/framework/utils"
|
||||||
|
import { MedusaRequest, MedusaStoreRequest } from "@medusajs/framework/http"
|
||||||
|
import {
|
||||||
|
wrapVariantsWithTotalInventoryQuantity,
|
||||||
|
wrapVariantsWithInventoryQuantityForSalesChannel,
|
||||||
|
} from "../variant-inventory-quantity"
|
||||||
|
|
||||||
|
jest.mock("@medusajs/framework/utils", () => {
|
||||||
|
const originalModule = jest.requireActual("@medusajs/framework/utils")
|
||||||
|
return {
|
||||||
|
...originalModule,
|
||||||
|
getTotalVariantAvailability: jest.fn(),
|
||||||
|
getVariantAvailability: jest.fn(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("variant-inventory-quantity", () => {
|
||||||
|
let req
|
||||||
|
let mockQuery
|
||||||
|
let variants
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockQuery = jest.fn()
|
||||||
|
variants = [
|
||||||
|
{ id: "variant-1", manage_inventory: true },
|
||||||
|
{ id: "variant-2", manage_inventory: true },
|
||||||
|
{ id: "variant-3", manage_inventory: false },
|
||||||
|
]
|
||||||
|
|
||||||
|
req = {
|
||||||
|
scope: {
|
||||||
|
resolve: jest.fn().mockReturnValue(mockQuery),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("wrapVariantsWithTotalInventoryQuantity", () => {
|
||||||
|
it("should not call getTotalVariantAvailability when variants array is empty", async () => {
|
||||||
|
await wrapVariantsWithTotalInventoryQuantity(req as MedusaRequest, [])
|
||||||
|
|
||||||
|
expect(getTotalVariantAvailability).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call getTotalVariantAvailability with correct parameters", async () => {
|
||||||
|
const mockAvailability = {
|
||||||
|
"variant-1": { availability: 10 },
|
||||||
|
"variant-2": { availability: 5 },
|
||||||
|
}
|
||||||
|
|
||||||
|
;(getTotalVariantAvailability as jest.Mock).mockResolvedValueOnce(
|
||||||
|
mockAvailability
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapVariantsWithTotalInventoryQuantity(
|
||||||
|
req as MedusaRequest,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(req.scope.resolve).toHaveBeenCalledWith(
|
||||||
|
ContainerRegistrationKeys.QUERY
|
||||||
|
)
|
||||||
|
expect(getTotalVariantAvailability).toHaveBeenCalledWith(mockQuery, {
|
||||||
|
variant_ids: ["variant-1", "variant-2", "variant-3"],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update inventory_quantity for variants with manage_inventory=true", async () => {
|
||||||
|
const mockAvailability = {
|
||||||
|
"variant-1": { availability: 10 },
|
||||||
|
"variant-2": { availability: 5 },
|
||||||
|
"variant-3": { availability: 20 },
|
||||||
|
}
|
||||||
|
|
||||||
|
;(getTotalVariantAvailability as jest.Mock).mockResolvedValueOnce(
|
||||||
|
mockAvailability
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapVariantsWithTotalInventoryQuantity(
|
||||||
|
req as MedusaRequest,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(variants[0].inventory_quantity).toBe(10)
|
||||||
|
expect(variants[1].inventory_quantity).toBe(5)
|
||||||
|
expect(variants[2].inventory_quantity).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("wrapVariantsWithInventoryQuantityForSalesChannel", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
scope: {
|
||||||
|
resolve: jest.fn().mockReturnValue(mockQuery),
|
||||||
|
},
|
||||||
|
publishable_key_context: {
|
||||||
|
sales_channel_ids: ["sc-1"],
|
||||||
|
},
|
||||||
|
validatedQuery: {},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when multiple sales channels are available and no single one is specified", async () => {
|
||||||
|
req.publishable_key_context.sales_channel_ids = ["sc-1", "sc-2"]
|
||||||
|
req.validatedQuery = { sales_channel_id: ["sc-1", "sc-2"] }
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
wrapVariantsWithInventoryQuantityForSalesChannel(
|
||||||
|
req as MedusaStoreRequest<unknown>,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
).rejects.toThrow(MedusaError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should use sales channel from query when single channel is specified", async () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: ["sc-2"] }
|
||||||
|
req.publishable_key_context = {
|
||||||
|
key: "test-key",
|
||||||
|
sales_channel_ids: ["sc-1", "sc-2"],
|
||||||
|
}
|
||||||
|
const mockAvailability = {
|
||||||
|
"variant-1": { availability: 7 },
|
||||||
|
"variant-2": { availability: 3 },
|
||||||
|
}
|
||||||
|
|
||||||
|
;(getVariantAvailability as jest.Mock).mockResolvedValueOnce(
|
||||||
|
mockAvailability
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapVariantsWithInventoryQuantityForSalesChannel(
|
||||||
|
req as MedusaStoreRequest<unknown>,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(getVariantAvailability).toHaveBeenCalledWith(mockQuery, {
|
||||||
|
variant_ids: ["variant-1", "variant-2", "variant-3"],
|
||||||
|
sales_channel_id: "sc-2",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should use sales channel from publishable key when single channel is available", async () => {
|
||||||
|
const mockAvailability = {
|
||||||
|
"variant-1": { availability: 12 },
|
||||||
|
"variant-2": { availability: 8 },
|
||||||
|
}
|
||||||
|
|
||||||
|
;(getVariantAvailability as jest.Mock).mockResolvedValueOnce(
|
||||||
|
mockAvailability
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapVariantsWithInventoryQuantityForSalesChannel(
|
||||||
|
req as MedusaStoreRequest<unknown>,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(getVariantAvailability).toHaveBeenCalledWith(mockQuery, {
|
||||||
|
variant_ids: ["variant-1", "variant-2", "variant-3"],
|
||||||
|
sales_channel_id: "sc-1",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle non-array sales_channel_id in query", async () => {
|
||||||
|
req.validatedQuery = { sales_channel_id: "sc-2" }
|
||||||
|
|
||||||
|
const originalPublishableKeyContext = deepCopy(
|
||||||
|
req.publishable_key_context
|
||||||
|
)
|
||||||
|
req.publishable_key_context = {
|
||||||
|
key: "test-key",
|
||||||
|
sales_channel_ids: ["sc-1", "sc-2"],
|
||||||
|
}
|
||||||
|
const mockAvailability = {
|
||||||
|
"variant-1": { availability: 7 },
|
||||||
|
"variant-2": { availability: 3 },
|
||||||
|
}
|
||||||
|
|
||||||
|
;(getVariantAvailability as jest.Mock).mockResolvedValueOnce(
|
||||||
|
mockAvailability
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapVariantsWithInventoryQuantityForSalesChannel(
|
||||||
|
req as MedusaStoreRequest<unknown>,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(getVariantAvailability).toHaveBeenCalledWith(mockQuery, {
|
||||||
|
variant_ids: ["variant-1", "variant-2", "variant-3"],
|
||||||
|
sales_channel_id: "sc-2",
|
||||||
|
})
|
||||||
|
|
||||||
|
req.publishable_key_context = originalPublishableKeyContext
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update inventory_quantity for variants with manage_inventory=true", async () => {
|
||||||
|
const mockAvailability = {
|
||||||
|
"variant-1": { availability: 15 },
|
||||||
|
"variant-2": { availability: 9 },
|
||||||
|
"variant-3": { availability: 25 },
|
||||||
|
}
|
||||||
|
|
||||||
|
;(getVariantAvailability as jest.Mock).mockResolvedValueOnce(
|
||||||
|
mockAvailability
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapVariantsWithInventoryQuantityForSalesChannel(
|
||||||
|
req as MedusaStoreRequest<unknown>,
|
||||||
|
variants
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(variants[0].inventory_quantity).toBe(15)
|
||||||
|
expect(variants[1].inventory_quantity).toBe(9)
|
||||||
|
expect(variants[2].inventory_quantity).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not call getVariantAvailability when variants array is empty", async () => {
|
||||||
|
await wrapVariantsWithInventoryQuantityForSalesChannel(
|
||||||
|
req as MedusaStoreRequest<unknown>,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(getVariantAvailability).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
+50
-33
@@ -2,48 +2,65 @@ import { MedusaStoreRequest } from "@medusajs/framework/http"
|
|||||||
import { arrayDifference, MedusaError } from "@medusajs/framework/utils"
|
import { arrayDifference, MedusaError } from "@medusajs/framework/utils"
|
||||||
import { NextFunction } from "express"
|
import { NextFunction } from "express"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms and validates the sales channel ids
|
||||||
|
* @param req
|
||||||
|
* @returns The transformed and validated sales channel ids
|
||||||
|
*/
|
||||||
|
export function transformAndValidateSalesChannelIds(
|
||||||
|
req: MedusaStoreRequest
|
||||||
|
): string[] {
|
||||||
|
const { sales_channel_ids: idsFromPublishableKey = [] } =
|
||||||
|
req.publishable_key_context
|
||||||
|
|
||||||
|
let { sales_channel_id: idsFromRequest = [] } = req.validatedQuery as {
|
||||||
|
sales_channel_id: string | string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
idsFromRequest = Array.isArray(idsFromRequest)
|
||||||
|
? idsFromRequest
|
||||||
|
: [idsFromRequest]
|
||||||
|
|
||||||
|
// If all sales channel ids are not in the publishable key, we throw an error
|
||||||
|
if (idsFromRequest.length) {
|
||||||
|
const uniqueInParams = arrayDifference(
|
||||||
|
idsFromRequest,
|
||||||
|
idsFromPublishableKey
|
||||||
|
)
|
||||||
|
|
||||||
|
if (uniqueInParams.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Requested sales channel is not part of the publishable key`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return idsFromRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idsFromPublishableKey?.length) {
|
||||||
|
return idsFromPublishableKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
// Selection of sales channels happens in the following priority:
|
// Selection of sales channels happens in the following priority:
|
||||||
// - If a publishable API key is passed, we take the sales channels attached to it and filter them down based on the query params
|
// - If a publishable API key is passed, we take the sales channels attached to it and filter them down based on the query params
|
||||||
// - If a sales channel id is passed through query params, we use that
|
// - If a sales channel id is passed through query params, we use that
|
||||||
// - If not, we use the default sales channel for the store
|
// - If not, we use the default sales channel for the store
|
||||||
export function filterByValidSalesChannels() {
|
export function filterByValidSalesChannels() {
|
||||||
return async (req: MedusaStoreRequest, _, next: NextFunction) => {
|
return async (req: MedusaStoreRequest, _, next: NextFunction) => {
|
||||||
const idsFromRequest = req.filterableFields.sales_channel_id
|
const salesChannelIds = transformAndValidateSalesChannelIds(req)
|
||||||
const { sales_channel_ids: idsFromPublishableKey = [] } =
|
|
||||||
req.publishable_key_context
|
|
||||||
|
|
||||||
// If all sales channel ids are not in the publishable key, we throw an error
|
if (!salesChannelIds.length) {
|
||||||
if (Array.isArray(idsFromRequest) && idsFromRequest.length) {
|
throw new MedusaError(
|
||||||
const uniqueInParams = arrayDifference(
|
|
||||||
idsFromRequest,
|
|
||||||
idsFromPublishableKey
|
|
||||||
)
|
|
||||||
|
|
||||||
if (uniqueInParams.length) {
|
|
||||||
return next(
|
|
||||||
new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Requested sales channel is not part of the publishable key`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.filterableFields.sales_channel_id = idsFromRequest
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idsFromPublishableKey?.length) {
|
|
||||||
req.filterableFields.sales_channel_id = idsFromPublishableKey
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(
|
|
||||||
new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Publishable key needs to have a sales channel configured`
|
`Publishable key needs to have a sales channel configured`
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
|
||||||
|
req.filterableFields.sales_channel_id = salesChannelIds
|
||||||
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
MedusaError,
|
MedusaError,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
import { MedusaRequest, MedusaStoreRequest } from "@medusajs/framework/http"
|
import { MedusaRequest, MedusaStoreRequest } from "@medusajs/framework/http"
|
||||||
|
import { transformAndValidateSalesChannelIds } from "./filter-by-valid-sales-channels"
|
||||||
|
|
||||||
export const wrapVariantsWithTotalInventoryQuantity = async (
|
export const wrapVariantsWithTotalInventoryQuantity = async (
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
@@ -28,25 +29,21 @@ export const wrapVariantsWithInventoryQuantityForSalesChannel = async (
|
|||||||
req: MedusaStoreRequest<unknown>,
|
req: MedusaStoreRequest<unknown>,
|
||||||
variants: VariantInput[]
|
variants: VariantInput[]
|
||||||
) => {
|
) => {
|
||||||
const salesChannelId = req.filterableFields.sales_channel_id as
|
const salesChannelIds = transformAndValidateSalesChannelIds(req)
|
||||||
| string
|
|
||||||
| string[]
|
|
||||||
const { sales_channel_ids: idsFromPublishableKey = [] } =
|
|
||||||
req.publishable_key_context
|
|
||||||
|
|
||||||
let channelToUse: string | undefined
|
const publishableApiKeySalesChannelIds =
|
||||||
if (salesChannelId && !Array.isArray(salesChannelId)) {
|
req.publishable_key_context.sales_channel_ids ?? []
|
||||||
channelToUse = salesChannelId
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idsFromPublishableKey.length === 1) {
|
let channelsToUse: string
|
||||||
channelToUse = idsFromPublishableKey[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!channelToUse) {
|
if (publishableApiKeySalesChannelIds.length === 1) {
|
||||||
|
channelsToUse = publishableApiKeySalesChannelIds[0]
|
||||||
|
} else if (salesChannelIds.length === 1) {
|
||||||
|
channelsToUse = salesChannelIds[0]
|
||||||
|
} else {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Inventory availability cannot be calculated in the given context. Either provide a sales channel id or configure a single sales channel in the publishable key`
|
`Inventory availability cannot be calculated in the given context. Either provide a single sales channel id or configure a single sales channel in the publishable key`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +57,7 @@ export const wrapVariantsWithInventoryQuantityForSalesChannel = async (
|
|||||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||||
const availability = await getVariantAvailability(query, {
|
const availability = await getVariantAvailability(query, {
|
||||||
variant_ids: variantIds,
|
variant_ids: variantIds,
|
||||||
sales_channel_id: channelToUse,
|
sales_channel_id: channelsToUse,
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapVariants(variants, availability)
|
wrapVariants(variants, availability)
|
||||||
|
|||||||
Reference in new issue
Block a user