fix(medusa): default sales channel for store variant endpoints (#4556)

* add changeset

* include default sales channel when querying variant endpoints

* make parameter optional

* update default values

* add integraiton tests
This commit is contained in:
Philip Korsholm
2023-09-11 14:05:47 +02:00
committed by GitHub
parent 87e3a7d06a
commit 2b078f06d9
7 changed files with 86 additions and 10 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
fix(medusa): include default sales channel for store/variant endpoints if no other channel is selected
@@ -10,7 +10,10 @@ const adminSeeder = require("../../../../helpers/admin-seeder")
jest.setTimeout(30000) jest.setTimeout(30000)
const { simpleProductFactory } = require("../../../../factories") const {
simpleProductFactory,
simpleSalesChannelFactory,
} = require("../../../../factories")
const adminHeaders = { headers: { Authorization: "Bearer test_token" } } const adminHeaders = { headers: { Authorization: "Bearer test_token" } }
@@ -21,6 +24,10 @@ describe("Get variant", () => {
const productId = "test-product" const productId = "test-product"
const variantId = "test-variant" const variantId = "test-variant"
let invItem let invItem
let salesChannelService
let salesChannelLocationService
let location
let inventoryService
beforeAll(async () => { beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", "..")) const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
@@ -52,8 +59,16 @@ describe("Get variant", () => {
const productVariantInventoryService = appContainer.resolve( const productVariantInventoryService = appContainer.resolve(
"productVariantInventoryService" "productVariantInventoryService"
) )
const inventoryService = appContainer.resolve("inventoryService") inventoryService = appContainer.resolve("inventoryService")
salesChannelService = appContainer.resolve("salesChannelService")
salesChannelLocationService = appContainer.resolve(
"salesChannelLocationService"
)
const stockLocationService = appContainer.resolve("stockLocationService")
location = await stockLocationService.create({
name: "test-location",
})
await simpleProductFactory( await simpleProductFactory(
dbConnection, dbConnection,
{ {
@@ -95,4 +110,34 @@ describe("Get variant", () => {
}) })
) )
}) })
it("sets availability correctly", async () => {
const salesChannel = await simpleSalesChannelFactory(dbConnection, {
is_default: true,
})
await salesChannelService.addProducts(salesChannel.id, [productId])
await salesChannelLocationService.associateLocation(
salesChannel.id,
location.id
)
await inventoryService.createInventoryLevel({
inventory_item_id: invItem.id,
location_id: location.id,
stocked_quantity: 10,
})
const api = useApi()
const response = await api.get(`/store/variants/${variantId}`)
expect(response.data).toEqual({
variant: expect.objectContaining({
purchasable: true,
inventory_quantity: 10,
}),
})
})
}) })
@@ -64,7 +64,9 @@ describe("List Variants", () => {
name: "test-location", name: "test-location",
}) })
const salesChannel = await simpleSalesChannelFactory(dbConnection, {}) const salesChannel = await simpleSalesChannelFactory(dbConnection, {
is_default: true,
})
const product = await simpleProductFactory(dbConnection, { const product = await simpleProductFactory(dbConnection, {
variants: [{ id: variantId }], variants: [{ id: variantId }],
@@ -125,5 +127,20 @@ describe("List Variants", () => {
}) })
) )
}) })
it("sets availability correctly", async () => {
const api = useApi()
const response = await api.get(`/store/variants?ids=${variantId}`)
expect(response.data).toEqual({
variants: [
expect.objectContaining({
purchasable: true,
inventory_quantity: 10,
}),
],
})
})
}) })
}) })
@@ -1,5 +1,6 @@
import { FlagRouter } from "@medusajs/utils"
import { NextFunction, Request, Response } from "express" import { NextFunction, Request, Response } from "express"
import { FlagRouter } from "@medusajs/utils"
import SalesChannelFeatureFlag from "../../loaders/feature-flags/sales-channels" import SalesChannelFeatureFlag from "../../loaders/feature-flags/sales-channels"
import { SalesChannelService } from "../../services" import { SalesChannelService } from "../../services"
@@ -8,11 +9,13 @@ import { SalesChannelService } from "../../services"
* @param context Object of options * @param context Object of options
* @param context.attachChannelAsArray Whether to attach the default sales channel as an array or just a string * @param context.attachChannelAsArray Whether to attach the default sales channel as an array or just a string
*/ */
export function withDefaultSalesChannel({ export function withDefaultSalesChannel(
attachChannelAsArray, {
}: { attachChannelAsArray,
attachChannelAsArray?: boolean }: {
}): (req: Request, res: Response, next: NextFunction) => Promise<void> { attachChannelAsArray: boolean
} = { attachChannelAsArray: false }
): (req: Request, res: Response, next: NextFunction) => Promise<void> {
return async (req: Request, _, next: NextFunction) => { return async (req: Request, _, next: NextFunction) => {
const featureFlagRouter = req.scope.resolve( const featureFlagRouter = req.scope.resolve(
"featureFlagRouter" "featureFlagRouter"
@@ -38,6 +41,7 @@ export function withDefaultSalesChannel({
: defaultSalesChannel.id : defaultSalesChannel.id
} }
} catch { } catch {
// noop
} finally { } finally {
next() next()
} }
@@ -40,7 +40,7 @@ export default (app, featureFlagRouter: FlagRouter) => {
route.get( route.get(
"/:id", "/:id",
withDefaultSalesChannel({}), withDefaultSalesChannel(),
transformStoreQuery(StoreGetProductsProductParams, { transformStoreQuery(StoreGetProductsProductParams, {
defaultRelations: defaultStoreProductsRelations, defaultRelations: defaultStoreProductsRelations,
defaultFields: defaultStoreProductsFields, defaultFields: defaultStoreProductsFields,
@@ -7,6 +7,7 @@ import { StoreGetVariantsVariantParams } from "./get-variant"
import { extendRequestParams } from "../../../middlewares/publishable-api-key/extend-request-params" import { extendRequestParams } from "../../../middlewares/publishable-api-key/extend-request-params"
import { validateProductVariantSalesChannelAssociation } from "../../../middlewares/publishable-api-key/validate-variant-sales-channel-association" import { validateProductVariantSalesChannelAssociation } from "../../../middlewares/publishable-api-key/validate-variant-sales-channel-association"
import { validateSalesChannelParam } from "../../../middlewares/publishable-api-key/validate-sales-channel-param" import { validateSalesChannelParam } from "../../../middlewares/publishable-api-key/validate-sales-channel-param"
import { withDefaultSalesChannel } from "../../../middlewares/with-default-sales-channel"
const route = Router() const route = Router()
@@ -17,6 +18,7 @@ export default (app) => {
route.get( route.get(
"/", "/",
withDefaultSalesChannel(),
transformStoreQuery(StoreGetVariantsParams, { transformStoreQuery(StoreGetVariantsParams, {
defaultRelations: defaultStoreVariantRelations, defaultRelations: defaultStoreVariantRelations,
allowedRelations: allowedStoreVariantRelations, allowedRelations: allowedStoreVariantRelations,
@@ -26,6 +28,7 @@ export default (app) => {
) )
route.get( route.get(
"/:id", "/:id",
withDefaultSalesChannel(),
transformStoreQuery(StoreGetVariantsVariantParams, { transformStoreQuery(StoreGetVariantsVariantParams, {
defaultRelations: defaultStoreVariantRelations, defaultRelations: defaultStoreVariantRelations,
allowedRelations: allowedStoreVariantRelations, allowedRelations: allowedStoreVariantRelations,
@@ -9,6 +9,7 @@ import { IsInt, IsOptional, IsString } from "class-validator"
import { FilterableProductVariantProps } from "../../../../types/product-variant" import { FilterableProductVariantProps } from "../../../../types/product-variant"
import { IsType } from "../../../../utils/validators/is-type" import { IsType } from "../../../../utils/validators/is-type"
import { MedusaError } from "@medusajs/utils"
import { NumericalComparisonOperator } from "../../../../types/common" import { NumericalComparisonOperator } from "../../../../types/common"
import { PriceSelectionParams } from "../../../../types/price-selection" import { PriceSelectionParams } from "../../../../types/price-selection"
import { Type } from "class-transformer" import { Type } from "class-transformer"
@@ -154,6 +155,7 @@ export default async (req, res) => {
"cart_id", "cart_id",
"region_id", "region_id",
"currency_code", "currency_code",
"sales_channel_id",
]) ])
if (validated.ids) { if (validated.ids) {