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: object notation in queryparams #1002

Merged
merged 3 commits into from
Feb 1, 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
77 changes: 77 additions & 0 deletions integration-tests/api/__tests__/admin/discount.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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()

Expand Down
25 changes: 15 additions & 10 deletions packages/medusa-interfaces/src/base-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -68,14 +73,14 @@ class BaseService {

return acc
}, {})

return where
}

const query = {
where: build(selector),
}

if ("deleted_at" in selector) {
query.withDeleted = true
}
Expand Down
44 changes: 33 additions & 11 deletions packages/medusa/src/api/routes/admin/discounts/list-discounts.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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
)

Expand All @@ -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()
Expand All @@ -77,6 +94,11 @@ export class AdminGetDiscountsParams {
@Transform(({ value }) => value === "true")
is_disabled?: boolean

@ValidateNested()
@IsOptional()
@Type(() => AdminGetDiscountsDiscountRuleParams)
rule?: AdminGetDiscountsDiscountRuleParams

@IsInt()
@IsOptional()
@Type(() => Number)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
6 changes: 4 additions & 2 deletions packages/medusa/src/models/discount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class Discount {

@Column({ nullable: true })
parent_discount_id: string

@ManyToOne(() => Discount)
@JoinColumn({ name: "parent_discount_id" })
parent_discount: Discount
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions packages/medusa/src/types/discount.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { IsEnum, IsOptional } from "class-validator"
import { AllocationType, DiscountRuleType } from "../models/discount-rule"

export type QuerySelector = {
q?: string
}
Expand Down