feat: /store api product types (#2552)

## What
Allow users to fetch ProductTypes from the storefront API.

## Why
This endpoint will allow developers to implement better faceted product search in Medusa without the need for search plugin. Developers will be able to use this to render refinement lists based on types, like this:
![image](https://user-images.githubusercontent.com/116003638/200417828-863065de-3607-49db-bd72-62a6815129fa.png)

## How
Endpoint `GET /store/products/types` and `GET /store/product-types` (use [product types listing in admin](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/api/routes/admin/products/list-types.ts) as reference)

Support added in @medusajs/medusa-js
Support added in medusa-react

## Testing
Similar automated tests as `GET /admin/products/types` and `GET /admin/product-types`

---

Resolves CORE-699
This commit is contained in:
Patrick
2022-11-09 11:10:17 -05:00
committed by GitHub
parent 2d095a0ce1
commit 7b0ceeffb4
18 changed files with 517 additions and 32 deletions

View File

@@ -55,11 +55,7 @@ describe("/admin/product-types", () => {
it("returns a list of product types", async () => {
const api = useApi()
const res = await api
.get("/admin/product-types", adminReqConfig)
.catch((err) => {
console.log(err)
})
const res = await api.get("/admin/product-types", adminReqConfig)
expect(res.status).toEqual(200)
@@ -74,11 +70,10 @@ describe("/admin/product-types", () => {
it("returns a list of product types matching free text search param", async () => {
const api = useApi()
const res = await api
.get("/admin/product-types?q=test-type-new", adminReqConfig)
.catch((err) => {
console.log(err)
})
const res = await api.get(
"/admin/product-types?q=test-type-new",
adminReqConfig
)
expect(res.status).toEqual(200)

View File

@@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/store/product-types GET /store/product-types returns a list of product types 1`] = `
Array [
Object {
"created_at": Any<String>,
"id": "test-type-new",
"updated_at": Any<String>,
"value": "test-type-new",
},
Object {
"created_at": Any<String>,
"id": "test-type",
"updated_at": Any<String>,
"value": "test-type",
},
]
`;
exports[`/store/product-types GET /store/product-types returns a list of product types matching free text search param 1`] = `
Array [
Object {
"created_at": Any<String>,
"id": "test-type-new",
"updated_at": Any<String>,
"value": "test-type-new",
},
]
`;

View File

@@ -0,0 +1,154 @@
const path = require("path")
const { IdMap } = require("medusa-test-utils")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const productSeeder = require("../../helpers/product-seeder")
const {
DiscountRuleType,
AllocationType,
DiscountConditionType,
DiscountConditionOperator,
} = require("@medusajs/medusa")
const { simpleDiscountFactory } = require("../../factories")
jest.setTimeout(50000)
describe("/store/product-types", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
describe("GET /store/product-types", () => {
beforeEach(async () => {
await productSeeder(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("returns a list of product types", async () => {
const api = useApi()
const res = await api.get("/store/product-types")
expect(res.status).toEqual(200)
const typeMatch = {
created_at: expect.any(String),
updated_at: expect.any(String),
}
expect(res.data.product_types).toMatchSnapshot([typeMatch, typeMatch])
})
it("returns a list of product types matching free text search param", async () => {
const api = useApi()
const res = await api.get("/store/product-types?q=test-type-new")
expect(res.status).toEqual(200)
const typeMatch = {
created_at: expect.any(String),
updated_at: expect.any(String),
}
// The value of the type should match the search param
expect(res.data.product_types.map((pt) => pt.value)).toEqual([
"test-type-new",
])
// Should only return one type as there is only one match to the search param
expect(res.data.product_types).toMatchSnapshot([typeMatch])
})
it("returns a list of product type filtered by discount condition id", async () => {
const api = useApi()
const resTypes = await api.get("/store/product-types")
const type1 = resTypes.data.product_types[0]
const type2 = resTypes.data.product_types[1]
const buildDiscountData = (code, conditionId, types) => {
return {
code,
rule: {
type: DiscountRuleType.PERCENTAGE,
value: 10,
allocation: AllocationType.TOTAL,
conditions: [
{
id: conditionId,
type: DiscountConditionType.PRODUCT_TYPES,
operator: DiscountConditionOperator.IN,
product_types: types,
},
],
},
}
}
const discountConditionId = IdMap.getId("discount-condition-type-1")
await simpleDiscountFactory(
dbConnection,
buildDiscountData("code-1", discountConditionId, [type1.id])
)
const discountConditionId2 = IdMap.getId("discount-condition-type-2")
await simpleDiscountFactory(
dbConnection,
buildDiscountData("code-2", discountConditionId2, [type2.id])
)
let res = await api.get(
`/store/product-types?discount_condition_id=${discountConditionId}`
)
expect(res.status).toEqual(200)
expect(res.data.product_types).toHaveLength(1)
expect(res.data.product_types).toEqual(
expect.arrayContaining([expect.objectContaining({ id: type1.id })])
)
res = await api.get(
`/store/product-types?discount_condition_id=${discountConditionId2}`
)
expect(res.status).toEqual(200)
expect(res.data.product_types).toHaveLength(1)
expect(res.data.product_types).toEqual(
expect.arrayContaining([expect.objectContaining({ id: type2.id })])
)
res = await api.get(`/store/product-types`)
expect(res.status).toEqual(200)
expect(res.data.product_types).toHaveLength(2)
expect(res.data.product_types).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: type1.id }),
expect.objectContaining({ id: type2.id }),
])
)
})
})
})