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:
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { IsEnum, IsOptional } from "class-validator"
|
||||
import { AllocationType, DiscountRuleType } from "../models/discount-rule"
|
||||
|
||||
export type QuerySelector = {
|
||||
q?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user