feat: Enable filtering admin products by variant EAN, UPC, and barcode (#12815)
* Add filters for variant ean, upc, and barcode in product queries and validators * fix: Omit 'q' field from variants in product list and validation parameters * Add tests for admin products filtering by variants ean, upc, and barcode * Add changeset for filter admin products api by variant ean, upc, and barcode --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
1 parent
fbf33885f5
commit
6ca755ede7
4 files changed
+255
-4
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"integration-tests-http": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Enable filtering admin products API by variant EAN, UPC, and barcode
|
||||||
@@ -949,6 +949,231 @@ medusaIntegrationTestRunner({
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("returns a list of products filtered by variants[ean]", async () => {
|
||||||
|
const productWithEan = await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
getProductFixture({
|
||||||
|
title: "Product with EAN",
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
ean: "1234567890123",
|
||||||
|
prices: [{ currency_code: "usd", amount: 100 }],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
getProductFixture({
|
||||||
|
title: "Product with different EAN",
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant 2",
|
||||||
|
ean: "9876543210987",
|
||||||
|
prices: [{ currency_code: "usd", amount: 150 }],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products?variants[ean]=1234567890123", adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.products).toHaveLength(1)
|
||||||
|
expect(response.data.products).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: productWithEan.data.product.id,
|
||||||
|
title: "Product with EAN",
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
ean: "1234567890123",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns a list of products filtered by variants[upc]", async () => {
|
||||||
|
const productWithUpc = await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
getProductFixture({
|
||||||
|
title: "Product with UPC",
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
upc: "123456789012",
|
||||||
|
prices: [{ currency_code: "usd", amount: 200 }],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
getProductFixture({
|
||||||
|
title: "Product with different UPC",
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant 2",
|
||||||
|
upc: "098765432109",
|
||||||
|
prices: [{ currency_code: "usd", amount: 250 }],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products?variants[upc]=123456789012", adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.products).toHaveLength(1)
|
||||||
|
expect(response.data.products).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: productWithUpc.data.product.id,
|
||||||
|
title: "Product with UPC",
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
upc: "123456789012",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns a list of products filtered by variants[barcode]", async () => {
|
||||||
|
const productWithBarcode = await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
getProductFixture({
|
||||||
|
title: "Product with Barcode",
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
barcode: "1234567890",
|
||||||
|
prices: [{ currency_code: "usd", amount: 300 }],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
getProductFixture({
|
||||||
|
title: "Product with different Barcode",
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant 2",
|
||||||
|
barcode: "0987654321",
|
||||||
|
prices: [{ currency_code: "usd", amount: 350 }],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products?variants[barcode]=1234567890", adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.products).toHaveLength(1)
|
||||||
|
expect(response.data.products).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: productWithBarcode.data.product.id,
|
||||||
|
title: "Product with Barcode",
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
barcode: "1234567890",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns empty list when filtering by non-existent variants[ean]", async () => {
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products?variants[ean]=5555555555555", adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.products).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns empty list when filtering by non-existent variants[upc]", async () => {
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products?variants[upc]=555555555555", adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.products).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns empty list when filtering by non-existent variants[barcode]", async () => {
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products?variants[barcode]=5555555555", adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.products).toHaveLength(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("GET /admin/products/:id", () => {
|
describe("GET /admin/products/:id", () => {
|
||||||
|
|||||||
@@ -24,6 +24,18 @@ export interface AdminProductVariantParams
|
|||||||
* out of stock.
|
* out of stock.
|
||||||
*/
|
*/
|
||||||
allow_backorder?: boolean
|
allow_backorder?: boolean
|
||||||
|
/**
|
||||||
|
* Filter by variant ean(s).
|
||||||
|
*/
|
||||||
|
ean?: string | string[]
|
||||||
|
/**
|
||||||
|
* Filter by variant upc(s).
|
||||||
|
*/
|
||||||
|
upc?: string | string[]
|
||||||
|
/**
|
||||||
|
* Filter by variant barcode(s).
|
||||||
|
*/
|
||||||
|
barcode?: string | string[]
|
||||||
/**
|
/**
|
||||||
* Apply filters on the variant's creation date.
|
* Apply filters on the variant's creation date.
|
||||||
*/
|
*/
|
||||||
@@ -46,5 +58,5 @@ export interface AdminProductListParams
|
|||||||
/**
|
/**
|
||||||
* Apply filters on the product variants.
|
* Apply filters on the product variants.
|
||||||
*/
|
*/
|
||||||
variants?: AdminProductVariantParams
|
variants?: Omit<AdminProductVariantParams, "q">
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,9 @@ export const AdminGetProductVariantsParamsFields = z.object({
|
|||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
manage_inventory: booleanString().optional(),
|
manage_inventory: booleanString().optional(),
|
||||||
allow_backorder: booleanString().optional(),
|
allow_backorder: booleanString().optional(),
|
||||||
|
ean: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
upc: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
barcode: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
created_at: createOperatorMap().optional(),
|
created_at: createOperatorMap().optional(),
|
||||||
updated_at: createOperatorMap().optional(),
|
updated_at: createOperatorMap().optional(),
|
||||||
deleted_at: createOperatorMap().optional(),
|
deleted_at: createOperatorMap().optional(),
|
||||||
@@ -41,9 +44,13 @@ export const AdminGetProductVariantsParams = createFindParams({
|
|||||||
.merge(applyAndAndOrOperators(AdminGetProductVariantsParamsFields))
|
.merge(applyAndAndOrOperators(AdminGetProductVariantsParamsFields))
|
||||||
|
|
||||||
export const AdminGetProductsParamsDirectFields = z.object({
|
export const AdminGetProductsParamsDirectFields = z.object({
|
||||||
variants: AdminGetProductVariantsParamsFields.merge(
|
variants: AdminGetProductVariantsParamsFields.omit({ q: true })
|
||||||
applyAndAndOrOperators(AdminGetProductVariantsParamsFields)
|
.merge(
|
||||||
).optional(),
|
applyAndAndOrOperators(
|
||||||
|
AdminGetProductVariantsParamsFields.omit({ q: true })
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
status: statusEnum.array().optional(),
|
status: statusEnum.array().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user