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:
Philip Korsholm
2022-02-01 17:31:12 +01:00
committed by GitHub
parent f115855282
commit 7fcdac51ea
7 changed files with 134 additions and 25 deletions

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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()

View File

@@ -1,3 +1,6 @@
import { IsEnum, IsOptional } from "class-validator"
import { AllocationType, DiscountRuleType } from "../models/discount-rule"
export type QuerySelector = {
q?: string
}