Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(medusa): Allow to query product types by discount condition id #2359

Merged
merged 5 commits into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lucky-worms-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---

feat(medusa): Allow to query product types by discount condition id
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ exports[`/admin/product-types GET /admin/product-types returns a list of product
Array [
Object {
"created_at": Any<String>,
"id": "test-type",
"id": "test-type-new",
"updated_at": Any<String>,
"value": "test-type",
"value": "test-type-new",
},
Object {
"created_at": Any<String>,
"id": "test-type-new",
"id": "test-type",
"updated_at": Any<String>,
"value": "test-type-new",
"value": "test-type",
},
]
`;
Expand Down
100 changes: 90 additions & 10 deletions integration-tests/api/__tests__/admin/product-type.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
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 adminSeeder = require("../../helpers/admin-seeder")
const productSeeder = require("../../helpers/product-seeder")
const {
DiscountRuleType,
AllocationType,
DiscountConditionType,
DiscountConditionOperator,
} = require("@medusajs/medusa")
const { simpleDiscountFactory } = require("../../factories")

jest.setTimeout(50000)

const adminReqConfig = {
headers: {
Authorization: "Bearer test_token",
},
}

describe("/admin/product-types", () => {
let medusaProcess
let dbConnection
Expand Down Expand Up @@ -41,11 +56,7 @@ describe("/admin/product-types", () => {
const api = useApi()

const res = await api
.get("/admin/product-types", {
headers: {
Authorization: "Bearer test_token",
},
})
.get("/admin/product-types", adminReqConfig)
.catch((err) => {
console.log(err)
})
Expand All @@ -64,11 +75,7 @@ describe("/admin/product-types", () => {
const api = useApi()

const res = await api
.get("/admin/product-types?q=test-type-new", {
headers: {
Authorization: "Bearer test_token",
},
})
.get("/admin/product-types?q=test-type-new", adminReqConfig)
.catch((err) => {
console.log(err)
})
Expand All @@ -88,5 +95,78 @@ describe("/admin/product-types", () => {
// 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("/admin/product-types", adminReqConfig)

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(
`/admin/product-types?discount_condition_id=${discountConditionId}`,
adminReqConfig
)

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(
`/admin/product-types?discount_condition_id=${discountConditionId2}`,
adminReqConfig
)

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(`/admin/product-types`, adminReqConfig)

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 }),
])
)
})
})
})
14 changes: 12 additions & 2 deletions packages/medusa/src/api/routes/admin/product-types/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { Router } from "express"
import { ProductType } from "../../../.."
import { PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
import middlewares, { transformQuery } from "../../../middlewares"
import "reflect-metadata"
import { AdminGetProductTypesParams } from "./list-product-types"

const route = Router()

export default (app) => {
app.use("/product-types", route)

route.get("/", middlewares.wrap(require("./list-product-types").default))
route.get(
"/",
transformQuery(AdminGetProductTypesParams, {
defaultFields: defaultAdminProductTypeFields,
defaultRelations: defaultAdminProductTypeRelations,
allowedFields: allowedAdminProductTypeFields,
isList: true,
}),
middlewares.wrap(require("./list-product-types").default)
)

return app
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import {
DateComparisonOperator,
FindConfig,
StringComparisonOperator,
} from "../../../../types/common"
import { IsNumber, IsOptional, IsString } from "class-validator"
import {
allowedAdminProductTypeFields,
defaultAdminProductTypeFields,
defaultAdminProductTypeRelations,
} from "."
import { identity, omit, pickBy } from "lodash"
import { identity, pickBy } from "lodash"

import { IsType } from "../../../../utils/validators/is-type"
import { MedusaError } from "medusa-core-utils"
import { ProductType } from "../../../../models/product-type"
import ProductTypeService from "../../../../services/product-type"
import { Type } from "class-transformer"
import { validator } from "../../../../utils/validator"
import { isDefined } from "../../../../utils"

/**
* @oas [get] /product-types
Expand All @@ -29,6 +19,7 @@ import { isDefined } from "../../../../utils"
* - (query) limit=10 {integer} The number of types to return.
* - (query) offset=0 {integer} The number of items to skip before the results.
* - (query) order {string} The field to sort items by.
* - (query) discount_condition_id {string} The discount condition id on which to filter the product types.
* - in: query
* name: value
* style: form
Expand Down Expand Up @@ -145,37 +136,11 @@ import { isDefined } from "../../../../utils"
* $ref: "#/components/responses/500_error"
*/
export default async (req, res) => {
const validated = await validator(AdminGetProductTypesParams, req.query)

const typeService: ProductTypeService =
req.scope.resolve("productTypeService")

const listConfig: FindConfig<ProductType> = {
select: defaultAdminProductTypeFields as (keyof ProductType)[],
relations: defaultAdminProductTypeRelations,
skip: validated.offset,
take: validated.limit,
}

if (isDefined(validated.order)) {
let orderField = validated.order
if (validated.order.startsWith("-")) {
const [, field] = validated.order.split("-")
orderField = field
listConfig.order = { [field]: "DESC" }
} else {
listConfig.order = { [validated.order]: "ASC" }
}

if (!allowedAdminProductTypeFields.includes(orderField)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Order field must be a valid product type field"
)
}
}

const filterableFields = omit(validated, ["limit", "offset"])
const { listConfig, filterableFields } = req
const { skip, take } = req.listConfig

const [types, count] = await typeService.listAndCount(
pickBy(filterableFields, identity),
Expand All @@ -185,8 +150,8 @@ export default async (req, res) => {
res.status(200).json({
product_types: types,
count,
offset: validated.offset,
limit: validated.limit,
offset: skip,
limit: take,
})
}

Expand Down Expand Up @@ -227,4 +192,8 @@ export class AdminGetProductTypesParams extends AdminGetProductTypesPaginationPa
@IsString()
@IsOptional()
order?: string

@IsString()
@IsOptional()
discount_condition_id?: string
}
33 changes: 31 additions & 2 deletions packages/medusa/src/repositories/product-type.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { EntityRepository, Repository } from "typeorm"
import { ProductType } from "../models/product-type"
import { ExtendedFindConfig } from "../types/common"

type UpsertTypeInput = Partial<ProductType> & {
value: string
}

@EntityRepository(ProductType)
export class ProductTypeRepository extends Repository<ProductType> {
async upsertType(type?: UpsertTypeInput): Promise<ProductType | null> {
Expand All @@ -22,8 +24,35 @@ export class ProductTypeRepository extends Repository<ProductType> {
const created = this.create({
value: type.value,
})
const result = await this.save(created)
return await this.save(created)
}

async findAndCountByDiscountConditionId(
conditionId: string,
query: ExtendedFindConfig<ProductType, Partial<ProductType>>
): Promise<[ProductType[], number]> {
const qb = this.createQueryBuilder("pt")

if (query?.select) {
qb.select(query.select.map((select) => `pt.${select}`))
}

if (query.skip) {
qb.skip(query.skip)
}

if (query.take) {
qb.take(query.take)
}

return result
return await qb
.where(query.where)
.innerJoin(
"discount_condition_product_type",
"dc_pt",
`dc_pt.product_type_id = pt.id AND dc_pt.condition_id = :dcId`,
{ dcId: conditionId }
)
.getManyAndCount()
}
}
Loading