Feat: object notation in queryparams (#1002)
* add object notation for queryparams * test rule.type query params in list-discount * pr feedback
This commit is contained in:
1 parent
f115855282
commit
7fcdac51ea
7 files changed
+123
-14
No files matched your search
@@ -37,6 +37,13 @@ describe("/admin/discounts", () => {
|
|||||||
value: 10,
|
value: 10,
|
||||||
allocation: "total",
|
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, {
|
await manager.insert(Discount, {
|
||||||
id: "test-discount",
|
id: "test-discount",
|
||||||
code: "TESTING",
|
code: "TESTING",
|
||||||
@@ -65,6 +72,13 @@ describe("/admin/discounts", () => {
|
|||||||
is_dynamic: false,
|
is_dynamic: false,
|
||||||
is_disabled: true,
|
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 () => {
|
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 () => {
|
it("lists dynamic discounts ", async () => {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,13 @@ class BaseService {
|
|||||||
case "gte":
|
case "gte":
|
||||||
subquery.push({ operator: ">=", value: val })
|
subquery.push({ operator: ">=", value: val })
|
||||||
break
|
break
|
||||||
|
default:
|
||||||
|
acc[key] = value
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (subquery.length) {
|
||||||
acc[key] = Raw(
|
acc[key] = Raw(
|
||||||
(a) =>
|
(a) =>
|
||||||
subquery
|
subquery
|
||||||
@@ -60,6 +64,7 @@ class BaseService {
|
|||||||
.join(" AND "),
|
.join(" AND "),
|
||||||
subquery.map((s) => s.value)
|
subquery.map((s) => s.value)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
acc[key] = value
|
acc[key] = value
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
import { Type, Transform } from "class-transformer"
|
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 { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||||
|
import {
|
||||||
|
AllocationType,
|
||||||
|
DiscountRuleType,
|
||||||
|
} from "../../../../models/discount-rule"
|
||||||
import DiscountService from "../../../../services/discount"
|
import DiscountService from "../../../../services/discount"
|
||||||
import { ListSelector } from "../../../../types/discount"
|
import { DateComparisonOperator } from "../../../../types/common"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
/**
|
/**
|
||||||
* @oas [get] /discounts
|
* @oas [get] /discounts
|
||||||
@@ -33,14 +45,6 @@ export default async (req, res) => {
|
|||||||
const validated = await validator(AdminGetDiscountsParams, req.query)
|
const validated = await validator(AdminGetDiscountsParams, req.query)
|
||||||
|
|
||||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
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 = {
|
const listConfig = {
|
||||||
select: defaultAdminDiscountsFields,
|
select: defaultAdminDiscountsFields,
|
||||||
@@ -49,8 +53,11 @@ export default async (req, res) => {
|
|||||||
take: validated.limit,
|
take: validated.limit,
|
||||||
order: { created_at: "DESC" },
|
order: { created_at: "DESC" },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filterableFields = _.omit(validated, ["limit", "offset", "expand"])
|
||||||
|
|
||||||
const [discounts, count] = await discountService.listAndCount(
|
const [discounts, count] = await discountService.listAndCount(
|
||||||
selector,
|
pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
||||||
listConfig
|
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 {
|
export class AdminGetDiscountsParams {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -77,6 +94,11 @@ export class AdminGetDiscountsParams {
|
|||||||
@Transform(({ value }) => value === "true")
|
@Transform(({ value }) => value === "true")
|
||||||
is_disabled?: boolean
|
is_disabled?: boolean
|
||||||
|
|
||||||
|
@ValidateNested()
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => AdminGetDiscountsDiscountRuleParams)
|
||||||
|
rule?: AdminGetDiscountsDiscountRuleParams
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export default async (req, res) => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const [orders, count] = await orderService.listAndCount(
|
const [orders, count] = await orderService.listAndCount(
|
||||||
pickBy(filterableFields, identity),
|
pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
||||||
listConfig
|
listConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export default async (req, res) => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const [products, count] = await productService.listAndCount(
|
const [products, count] = await productService.listAndCount(
|
||||||
_.pickBy(filterableFields, identity),
|
_.pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
||||||
listConfig
|
listConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,9 @@ export class Discount {
|
|||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private beforeInsert() {
|
private beforeInsert() {
|
||||||
if (this.id) return
|
if (this.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const id = ulid()
|
const id = ulid()
|
||||||
this.id = `disc_${id}`
|
this.id = `disc_${id}`
|
||||||
this.code = this.code.toUpperCase()
|
this.code = this.code.toUpperCase()
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { IsEnum, IsOptional } from "class-validator"
|
||||||
|
import { AllocationType, DiscountRuleType } from "../models/discount-rule"
|
||||||
|
|
||||||
export type QuerySelector = {
|
export type QuerySelector = {
|
||||||
q?: string
|
q?: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user