From 7fcdac51ea9c1395cdb54adc148ef94af10f6ee4 Mon Sep 17 00:00:00 2001 From: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> Date: Tue, 1 Feb 2022 17:31:12 +0100 Subject: [PATCH] Feat: object notation in queryparams (#1002) * add object notation for queryparams * test rule.type query params in list-discount * pr feedback --- .../api/__tests__/admin/discount.js | 77 +++++++++++++++++++ .../medusa-interfaces/src/base-service.js | 25 +++--- .../routes/admin/discounts/list-discounts.ts | 44 ++++++++--- .../api/routes/admin/orders/list-orders.ts | 2 +- .../routes/admin/products/list-products.ts | 2 +- packages/medusa/src/models/discount.ts | 6 +- packages/medusa/src/types/discount.ts | 3 + 7 files changed, 134 insertions(+), 25 deletions(-) diff --git a/integration-tests/api/__tests__/admin/discount.js b/integration-tests/api/__tests__/admin/discount.js index aadc5140a8..e58772f865 100644 --- a/integration-tests/api/__tests__/admin/discount.js +++ b/integration-tests/api/__tests__/admin/discount.js @@ -37,6 +37,13 @@ describe("/admin/discounts", () => { value: 10, allocation: "total", }) + await manager.insert(DiscountRule, { + id: "test-discount-rule-fixed", + description: "Test discount rule", + type: "fixed", + value: 10, + allocation: "total", + }) await manager.insert(Discount, { id: "test-discount", code: "TESTING", @@ -65,6 +72,13 @@ describe("/admin/discounts", () => { is_dynamic: false, is_disabled: true, }) + await manager.insert(Discount, { + id: "fixed-discount", + code: "fixed100", + rule_id: "test-discount-rule-fixed", + is_dynamic: false, + is_disabled: false, + }) }) afterEach(async () => { @@ -96,6 +110,69 @@ describe("/admin/discounts", () => { ) }) + it("lists fixed discounts", async () => { + const api = useApi() + + const response = await api + .get("/admin/discounts?rule[type]=fixed", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + expect(response.status).toEqual(200) + expect(response.data.count).toEqual(1) + expect(response.data.discounts).toEqual([ + expect.objectContaining({ + id: "fixed-discount", + code: "fixed100", + }), + ]) + }) + + it("fails when listing invalid discount types", async () => { + expect.assertions(3) + const api = useApi() + + await api + .get("/admin/discounts?rule[type]=blah", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + expect(err.response.status).toEqual(400) + expect(err.response.data.type).toEqual("invalid_data") + expect(err.response.data.message).toEqual( + "type must be a valid enum value" + ) + }) + }) + + it("lists percentage discounts ", async () => { + const api = useApi() + + const notExpected = expect.objectContaining({ + rule: expect.objectContaining({ type: "fixed" }), + }) + + const response = await api + .get("/admin/discounts?rule[type]=percentage", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + expect(response.status).toEqual(200) + expect(response.data.discounts).toEqual( + expect.not.arrayContaining([notExpected]) + ) + }) + it("lists dynamic discounts ", async () => { const api = useApi() diff --git a/packages/medusa-interfaces/src/base-service.js b/packages/medusa-interfaces/src/base-service.js index 5adcbe0391..cee1271e23 100644 --- a/packages/medusa-interfaces/src/base-service.js +++ b/packages/medusa-interfaces/src/base-service.js @@ -21,7 +21,7 @@ class BaseService { buildQuery_(selector, config = {}) { const build = (obj) => { const where = Object.entries(obj).reduce((acc, [key, value]) => { - // Undefined values indicate that they have no significance to the query. + // Undefined values indicate that they have no significance to the query. // If the query is looking for rows where a column is not set it should use null instead of undefined if (typeof value === "undefined") { return acc @@ -50,16 +50,21 @@ class BaseService { case "gte": subquery.push({ operator: ">=", value: val }) break + default: + acc[key] = value + break } }) - acc[key] = Raw( - (a) => - subquery - .map((s, index) => `${a} ${s.operator} :${index}`) - .join(" AND "), - subquery.map((s) => s.value) - ) + if (subquery.length) { + acc[key] = Raw( + (a) => + subquery + .map((s, index) => `${a} ${s.operator} :${index}`) + .join(" AND "), + subquery.map((s) => s.value) + ) + } break default: acc[key] = value @@ -68,14 +73,14 @@ class BaseService { return acc }, {}) - + return where } const query = { where: build(selector), } - + if ("deleted_at" in selector) { query.withDeleted = true } diff --git a/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts b/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts index ecfc33a44b..11b2724273 100644 --- a/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts +++ b/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts @@ -1,8 +1,20 @@ import { Type, Transform } from "class-transformer" -import { IsBoolean, IsInt, IsOptional, IsString } from "class-validator" +import { + IsBoolean, + IsEnum, + IsInt, + IsOptional, + IsString, + ValidateNested, +} from "class-validator" +import _, { pickBy } from "lodash" import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "." +import { + AllocationType, + DiscountRuleType, +} from "../../../../models/discount-rule" import DiscountService from "../../../../services/discount" -import { ListSelector } from "../../../../types/discount" +import { DateComparisonOperator } from "../../../../types/common" import { validator } from "../../../../utils/validator" /** * @oas [get] /discounts @@ -33,14 +45,6 @@ export default async (req, res) => { const validated = await validator(AdminGetDiscountsParams, req.query) const discountService: DiscountService = req.scope.resolve("discountService") - const selector: ListSelector = {} - - if (validated.q) { - selector.q = validated.q - } - - selector.is_disabled = validated.is_disabled - selector.is_dynamic = validated.is_dynamic const listConfig = { select: defaultAdminDiscountsFields, @@ -49,8 +53,11 @@ export default async (req, res) => { take: validated.limit, order: { created_at: "DESC" }, } + + const filterableFields = _.omit(validated, ["limit", "offset", "expand"]) + const [discounts, count] = await discountService.listAndCount( - selector, + pickBy(filterableFields, (val) => typeof val !== "undefined"), listConfig ) @@ -62,6 +69,16 @@ export default async (req, res) => { }) } +class AdminGetDiscountsDiscountRuleParams { + @IsOptional() + @IsEnum(DiscountRuleType) + type: DiscountRuleType + + @IsOptional() + @IsEnum(AllocationType) + allocation: AllocationType +} + export class AdminGetDiscountsParams { @IsString() @IsOptional() @@ -77,6 +94,11 @@ export class AdminGetDiscountsParams { @Transform(({ value }) => value === "true") is_disabled?: boolean + @ValidateNested() + @IsOptional() + @Type(() => AdminGetDiscountsDiscountRuleParams) + rule?: AdminGetDiscountsDiscountRuleParams + @IsInt() @IsOptional() @Type(() => Number) diff --git a/packages/medusa/src/api/routes/admin/orders/list-orders.ts b/packages/medusa/src/api/routes/admin/orders/list-orders.ts index f453018f91..307e2dcbef 100644 --- a/packages/medusa/src/api/routes/admin/orders/list-orders.ts +++ b/packages/medusa/src/api/routes/admin/orders/list-orders.ts @@ -80,7 +80,7 @@ export default async (req, res) => { ]) const [orders, count] = await orderService.listAndCount( - pickBy(filterableFields, identity), + pickBy(filterableFields, (val) => typeof val !== "undefined"), listConfig ) diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 3fece75fd3..7b03a314c3 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -96,7 +96,7 @@ export default async (req, res) => { ]) const [products, count] = await productService.listAndCount( - _.pickBy(filterableFields, identity), + _.pickBy(filterableFields, (val) => typeof val !== "undefined"), listConfig ) diff --git a/packages/medusa/src/models/discount.ts b/packages/medusa/src/models/discount.ts index 64ac6c29c5..0ca2e8ebdb 100644 --- a/packages/medusa/src/models/discount.ts +++ b/packages/medusa/src/models/discount.ts @@ -44,7 +44,7 @@ export class Discount { @Column({ nullable: true }) parent_discount_id: string - + @ManyToOne(() => Discount) @JoinColumn({ name: "parent_discount_id" }) parent_discount: Discount @@ -95,7 +95,9 @@ export class Discount { @BeforeInsert() private beforeInsert() { - if (this.id) return + if (this.id) { + return + } const id = ulid() this.id = `disc_${id}` this.code = this.code.toUpperCase() diff --git a/packages/medusa/src/types/discount.ts b/packages/medusa/src/types/discount.ts index 0fabd32063..20e2355dba 100644 --- a/packages/medusa/src/types/discount.ts +++ b/packages/medusa/src/types/discount.ts @@ -1,3 +1,6 @@ +import { IsEnum, IsOptional } from "class-validator" +import { AllocationType, DiscountRuleType } from "../models/discount-rule" + export type QuerySelector = { q?: string }