feat: Product filtering (#439)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`/admin/discounts creates admin session correctly 1`] = `
|
||||
exports[`/admin/auth creates admin session correctly 1`] = `
|
||||
Object {
|
||||
"api_token": "test_token",
|
||||
"created_at": Any<String>,
|
||||
@@ -13,4 +13,3 @@ Object {
|
||||
"updated_at": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -380,5 +380,198 @@ Array [
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
Object {
|
||||
"collection": Any<Object>,
|
||||
"collection_id": "test-collection1",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"handle": "test-product_filtering_3",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^test-\\*/,
|
||||
"images": Array [],
|
||||
"is_giftcard": false,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Any<Array>,
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"status": "draft",
|
||||
"subtitle": null,
|
||||
"tags": Any<Array>,
|
||||
"thumbnail": null,
|
||||
"title": "Test product filtering 3",
|
||||
"type": Any<Object>,
|
||||
"type_id": "test-type",
|
||||
"updated_at": Any<String>,
|
||||
"variants": Any<Array>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
Object {
|
||||
"collection": Any<Object>,
|
||||
"collection_id": "test-collection1",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"handle": "test-product_filtering_1",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^test-\\*/,
|
||||
"images": Array [],
|
||||
"is_giftcard": false,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Any<Array>,
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"status": "proposed",
|
||||
"subtitle": null,
|
||||
"tags": Any<Array>,
|
||||
"thumbnail": null,
|
||||
"title": "Test product filtering 1",
|
||||
"type": Any<Object>,
|
||||
"type_id": "test-type",
|
||||
"updated_at": Any<String>,
|
||||
"variants": Any<Array>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
Object {
|
||||
"collection": Any<Object>,
|
||||
"collection_id": "test-collection2",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"handle": "test-product_filtering_2",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^test-\\*/,
|
||||
"images": Array [],
|
||||
"is_giftcard": false,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Any<Array>,
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"status": "published",
|
||||
"subtitle": null,
|
||||
"tags": Any<Array>,
|
||||
"thumbnail": null,
|
||||
"title": "Test product filtering 2",
|
||||
"type": Any<Object>,
|
||||
"type_id": "test-type",
|
||||
"updated_at": Any<String>,
|
||||
"variants": Any<Array>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`/admin/products GET /admin/products returns a list of products with giftcard in list 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"collection": null,
|
||||
"collection_id": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-giftcard-description",
|
||||
"discountable": false,
|
||||
"handle": "test-giftcard",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^prod_\\*/,
|
||||
"images": Array [],
|
||||
"is_giftcard": true,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"title": "Denominations",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"status": "draft",
|
||||
"subtitle": null,
|
||||
"tags": Array [],
|
||||
"thumbnail": null,
|
||||
"title": "Test Giftcard",
|
||||
"type": null,
|
||||
"type_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variants": Array [
|
||||
Object {
|
||||
"allow_backorder": false,
|
||||
"barcode": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"ean": null,
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^variant_\\*/,
|
||||
"inventory_quantity": 0,
|
||||
"length": null,
|
||||
"manage_inventory": true,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"option_id": StringMatching /\\^opt_\\*/,
|
||||
"updated_at": Any<String>,
|
||||
"value": "100",
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"prices": Array [
|
||||
Object {
|
||||
"amount": 100,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "usd",
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"region_id": null,
|
||||
"sale_amount": null,
|
||||
"updated_at": Any<String>,
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"sku": null,
|
||||
"title": "Test variant",
|
||||
"upc": null,
|
||||
"updated_at": Any<String>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
],
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -46,7 +46,35 @@ describe("/admin/products", () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api
|
||||
.get("/admin/products?status%5B%5D=null", {
|
||||
.get("/admin/products", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-product",
|
||||
status: "draft",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product1",
|
||||
status: "draft",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("returns a list of all products when no query is provided", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api
|
||||
.get("/admin/products?q=", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
@@ -89,7 +117,7 @@ describe("/admin/products", () => {
|
||||
})
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?status%5B%5D=proposed", {
|
||||
.get("/admin/products?status[]=proposed", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
@@ -109,6 +137,258 @@ describe("/admin/products", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("returns a list of products where status is proposed or published", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpected = [
|
||||
expect.objectContaining({ status: "draft" }),
|
||||
expect.objectContaining({ status: "rejected" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?status[]=published,proposed", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_1",
|
||||
status: "proposed",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_2",
|
||||
status: "published",
|
||||
}),
|
||||
])
|
||||
|
||||
for (const notExpect of notExpected) {
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([notExpect])
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products in collection", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpected = [
|
||||
expect.objectContaining({ collection_id: "test-collection" }),
|
||||
expect.objectContaining({ collection_id: "test-collection2" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?collection_id[]=test-collection1", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_1",
|
||||
collection_id: "test-collection1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_3",
|
||||
collection_id: "test-collection1",
|
||||
}),
|
||||
])
|
||||
|
||||
for (const notExpect of notExpected) {
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([notExpect])
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products with tags", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpected = [
|
||||
expect.objectContaining({ id: "tag1" }),
|
||||
expect.objectContaining({ id: "tag2" }),
|
||||
expect.objectContaining({ id: "tag4" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?tags[]=tag3", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_1",
|
||||
tags: [expect.objectContaining({ id: "tag3" })],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_2",
|
||||
tags: [expect.objectContaining({ id: "tag3" })],
|
||||
}),
|
||||
])
|
||||
for (const product of response.data.products) {
|
||||
for (const notExpect of notExpected) {
|
||||
expect(product.tags).toEqual(expect.not.arrayContaining([notExpect]))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products with tags in a collection", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpectedTags = [
|
||||
expect.objectContaining({ id: "tag1" }),
|
||||
expect.objectContaining({ id: "tag2" }),
|
||||
expect.objectContaining({ id: "tag3" }),
|
||||
]
|
||||
|
||||
const notExpectedCollections = [
|
||||
expect.objectContaining({ collection_id: "test-collection" }),
|
||||
expect.objectContaining({ collection_id: "test-collection2" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?collection_id[]=test-collection1&tags[]=tag4", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_3",
|
||||
collection_id: "test-collection1",
|
||||
tags: [expect.objectContaining({ id: "tag4" })],
|
||||
}),
|
||||
])
|
||||
|
||||
for (const notExpect of notExpectedCollections) {
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([notExpect])
|
||||
)
|
||||
}
|
||||
|
||||
for (const product of response.data.products) {
|
||||
for (const notExpect of notExpectedTags) {
|
||||
expect(product.tags).toEqual(expect.not.arrayContaining([notExpect]))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products with giftcard in list", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const payload = {
|
||||
title: "Test Giftcard",
|
||||
is_giftcard: true,
|
||||
description: "test-giftcard-description",
|
||||
options: [{ title: "Denominations" }],
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
prices: [{ currency_code: "usd", amount: 100 }],
|
||||
options: [{ value: "100" }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
await api
|
||||
.post("/admin/products", payload, {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?is_giftcard=true", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([
|
||||
expect.objectContaining({ is_giftcard: false }),
|
||||
])
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toMatchSnapshot([
|
||||
{
|
||||
title: "Test Giftcard",
|
||||
id: expect.stringMatching(/^prod_*/),
|
||||
is_giftcard: true,
|
||||
description: "test-giftcard-description",
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
options: [
|
||||
{
|
||||
title: "Denominations",
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
id: expect.stringMatching(/^variant_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
prices: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
created_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("returns a list of products with child entities", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -306,6 +586,42 @@ describe("/admin/products", () => {
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^test-*/),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
created_at: expect.any(String),
|
||||
type: expect.any(Object),
|
||||
collection: expect.any(Object),
|
||||
options: expect.any(Array),
|
||||
tags: expect.any(Array),
|
||||
variants: expect.any(Array),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^test-*/),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
created_at: expect.any(String),
|
||||
type: expect.any(Object),
|
||||
collection: expect.any(Object),
|
||||
options: expect.any(Array),
|
||||
tags: expect.any(Array),
|
||||
variants: expect.any(Array),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^test-*/),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
created_at: expect.any(String),
|
||||
type: expect.any(Object),
|
||||
collection: expect.any(Object),
|
||||
options: expect.any(Array),
|
||||
tags: expect.any(Array),
|
||||
variants: expect.any(Array),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`/admin/discounts creates store session correctly 1`] = `
|
||||
exports[`/admin/auth creates store session correctly 1`] = `
|
||||
Object {
|
||||
"billing_address_id": null,
|
||||
"created_at": Any<String>,
|
||||
@@ -15,4 +15,4 @@ Object {
|
||||
"phone": "12345678",
|
||||
"updated_at": Any<String>,
|
||||
}
|
||||
`;
|
||||
`;
|
||||
@@ -152,16 +152,16 @@ describe("/store/carts", () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "CREATED" }],
|
||||
let response = await api
|
||||
.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "SPENT" }],
|
||||
})
|
||||
.catch((error) => {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Discount has been used maximum allowed times"
|
||||
)
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Discount has been used maximum allowed times"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("fails to apply expired discount", async () => {
|
||||
|
||||
@@ -108,6 +108,28 @@ module.exports = async (connection, data = {}) => {
|
||||
tenPercent.rule = tenPercentRule
|
||||
await manager.save(tenPercent)
|
||||
|
||||
const dUsageLimit = await manager.create(Discount, {
|
||||
id: "test-discount-usage-limit",
|
||||
code: "SPENT",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
usage_limit: 10,
|
||||
usage_count: 10,
|
||||
})
|
||||
|
||||
const drUsage = await manager.create(DiscountRule, {
|
||||
id: "test-discount-rule-usage-limit",
|
||||
description: "Created",
|
||||
type: "fixed",
|
||||
value: 10000,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
dUsageLimit.rule = drUsage
|
||||
dUsageLimit.regions = [r]
|
||||
|
||||
await manager.save(dUsageLimit)
|
||||
|
||||
const d = await manager.create(Discount, {
|
||||
id: "test-discount",
|
||||
code: "CREATED",
|
||||
|
||||
@@ -25,6 +25,22 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(coll)
|
||||
|
||||
const coll1 = manager.create(ProductCollection, {
|
||||
id: "test-collection1",
|
||||
handle: "test-collection1",
|
||||
title: "Test collection 1",
|
||||
})
|
||||
|
||||
await manager.save(coll1)
|
||||
|
||||
const coll2 = manager.create(ProductCollection, {
|
||||
id: "test-collection2",
|
||||
handle: "test-collection2",
|
||||
title: "Test collection 2",
|
||||
})
|
||||
|
||||
await manager.save(coll2)
|
||||
|
||||
const tag = manager.create(ProductTag, {
|
||||
id: "tag1",
|
||||
value: "123",
|
||||
@@ -32,6 +48,20 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(tag)
|
||||
|
||||
const tag3 = manager.create(ProductTag, {
|
||||
id: "tag3",
|
||||
value: "123",
|
||||
})
|
||||
|
||||
await manager.save(tag3)
|
||||
|
||||
const tag4 = manager.create(ProductTag, {
|
||||
id: "tag4",
|
||||
value: "123",
|
||||
})
|
||||
|
||||
await manager.save(tag4)
|
||||
|
||||
const type = manager.create(ProductType, {
|
||||
id: "test-type",
|
||||
value: "test-type",
|
||||
@@ -199,4 +229,46 @@ module.exports = async (connection, data = {}) => {
|
||||
})
|
||||
|
||||
await manager.save(variant5)
|
||||
|
||||
const product1 = manager.create(Product, {
|
||||
id: "test-product_filtering_1",
|
||||
handle: "test-product_filtering_1",
|
||||
title: "Test product filtering 1",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
collection_id: "test-collection1",
|
||||
status: "proposed",
|
||||
tags: [{ id: "tag3", value: "123" }],
|
||||
})
|
||||
|
||||
await manager.save(product1)
|
||||
|
||||
const product2 = manager.create(Product, {
|
||||
id: "test-product_filtering_2",
|
||||
handle: "test-product_filtering_2",
|
||||
title: "Test product filtering 2",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
collection_id: "test-collection2",
|
||||
status: "published",
|
||||
tags: [{ id: "tag3", value: "123" }],
|
||||
})
|
||||
|
||||
await manager.save(product2)
|
||||
|
||||
const product3 = manager.create(Product, {
|
||||
id: "test-product_filtering_3",
|
||||
handle: "test-product_filtering_3",
|
||||
title: "Test product filtering 3",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
collection_id: "test-collection1",
|
||||
status: "draft",
|
||||
tags: [{ id: "tag4", value: "1234" }],
|
||||
})
|
||||
|
||||
await manager.save(product3)
|
||||
}
|
||||
|
||||
@@ -96,4 +96,33 @@ Joi.orderFilter = () => {
|
||||
})
|
||||
}
|
||||
|
||||
Joi.productFilter = () => {
|
||||
return Joi.object().keys({
|
||||
id: Joi.string(),
|
||||
q: Joi.string().allow(null, ""),
|
||||
status: Joi.array()
|
||||
.items(Joi.string().valid("proposed", "draft", "published", "rejected"))
|
||||
.single(),
|
||||
collection_id: Joi.array()
|
||||
.items(Joi.string())
|
||||
.single(),
|
||||
tags: Joi.array()
|
||||
.items(Joi.string())
|
||||
.single(),
|
||||
title: Joi.string(),
|
||||
description: Joi.string(),
|
||||
handle: Joi.string(),
|
||||
is_giftcard: Joi.string(),
|
||||
type: Joi.string(),
|
||||
offset: Joi.string(),
|
||||
limit: Joi.string(),
|
||||
expand: Joi.string(),
|
||||
fields: Joi.string(),
|
||||
order: Joi.string().optional(),
|
||||
created_at: Joi.dateFilter(),
|
||||
updated_at: Joi.dateFilter(),
|
||||
deleted_at: Joi.dateFilter(),
|
||||
})
|
||||
}
|
||||
|
||||
export default Joi
|
||||
|
||||
@@ -46,7 +46,11 @@ export default app => {
|
||||
)
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-product").default))
|
||||
route.get("/", middlewares.wrap(require("./list-products").default))
|
||||
route.get(
|
||||
"/",
|
||||
middlewares.normalizeQuery(),
|
||||
middlewares.wrap(require("./list-products").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -121,3 +125,18 @@ export const allowedRelations = [
|
||||
"type",
|
||||
"collection",
|
||||
]
|
||||
|
||||
export const filterableFields = [
|
||||
"id",
|
||||
"status",
|
||||
"collection_id",
|
||||
"tags",
|
||||
"title",
|
||||
"description",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"type",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from "lodash"
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { defaultFields, defaultRelations, filterableFields } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /products
|
||||
@@ -31,6 +31,17 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* $ref: "#/components/schemas/product"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.productFilter()
|
||||
|
||||
const { value, error } = schema.validate(req.query)
|
||||
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
JSON.stringify(error.details)
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
|
||||
@@ -53,21 +64,16 @@ export default async (req, res) => {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
if ("is_giftcard" in req.query) {
|
||||
selector.is_giftcard = req.query.is_giftcard === "true"
|
||||
for (const k of filterableFields) {
|
||||
if (k in value) {
|
||||
selector[k] = value[k]
|
||||
}
|
||||
}
|
||||
|
||||
if ("status" in req.query) {
|
||||
const schema = Validator.array()
|
||||
.items(
|
||||
Validator.string().valid("proposed", "draft", "published", "rejected")
|
||||
)
|
||||
.single()
|
||||
|
||||
const { value, error } = schema.validate(req.query.status)
|
||||
|
||||
if (value && !error) {
|
||||
selector.status = value
|
||||
if (selector.status?.indexOf("null") > -1) {
|
||||
selector.status.splice(selector.status.indexOf("null"), 1)
|
||||
if (selector.status.length === 0) {
|
||||
delete selector.status
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,27 @@ export class ProductRepository extends Repository<Product> {
|
||||
if (Array.isArray(idsOrOptionsWithoutRelations)) {
|
||||
entities = await this.findByIds(idsOrOptionsWithoutRelations)
|
||||
} else {
|
||||
entities = await this.find(idsOrOptionsWithoutRelations)
|
||||
// Since tags are in a one-to-many realtion they cant be included in a
|
||||
// regular query, to solve this add the join on tags seperately if
|
||||
// the query exists
|
||||
const tags = idsOrOptionsWithoutRelations.where.tags
|
||||
delete idsOrOptionsWithoutRelations.where.tags
|
||||
let qb = this.createQueryBuilder("product")
|
||||
.select(["product.id"])
|
||||
.where(idsOrOptionsWithoutRelations.where)
|
||||
.skip(idsOrOptionsWithoutRelations.skip)
|
||||
.take(idsOrOptionsWithoutRelations.take)
|
||||
|
||||
if (tags) {
|
||||
qb = qb
|
||||
.leftJoinAndSelect("product.tags", "tags")
|
||||
.andWhere(
|
||||
`tags.id IN (:...ids)`, { ids: tags._value}
|
||||
)
|
||||
}
|
||||
|
||||
entities = await qb
|
||||
.getMany()
|
||||
}
|
||||
const entitiesIds = entities.map(({ id }) => id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user