feat(medusa): Add support for filtering with gt, lt, gte and lte (#190)

- Add support for filtering with lt, gt, lte, gte in the following format: created_at[gt]=01-26-1997
- Add middleware for cleaning request queries
- Add order query validation
- Temp. disables tags in claim integration tests
This commit is contained in:
Oliver Windall Juhl authored and GitHub committed 2021-03-01 11:32:17 +01:00
1 parent bb7dcbd706
commit dd0491f521
8 files changed
+357 -71

No files matched your search

@@ -17,4 +17,72 @@ Joi.address = () => {
})
}
Joi.dateFilter = () => {
return Joi.object({
lt: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
gt: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
gte: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
lte: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
})
}
Joi.orderFilter = () => {
return Joi.object().keys({
id: Joi.string(),
status: Joi.array()
.items(
Joi.string().valid(
"pending",
"completed",
"archived",
"canceled",
"requires_action"
)
)
.single(),
fulfillment_status: Joi.array()
.items(
Joi.string().valid(
"not_fulfilled",
"fulfilled",
"partially_fulfilled",
"shipped",
"partially_shipped",
"canceled",
"returned",
"partially_returned",
"requires_action"
)
)
.single(),
payment_status: Joi.array()
.items(
Joi.string().valid(
"captured",
"awaiting",
"not_paid",
"refunded",
"partially_refunded",
"canceled",
"requires_action"
)
)
.single(),
display_id: Joi.string(),
cart_id: Joi.string(),
offset: Joi.string(),
limit: Joi.string(),
expand: Joi.string(),
fields: Joi.string(),
customer_id: Joi.string(),
email: Joi.string(),
region_id: Joi.string(),
currency_code: Joi.string(),
tax_rate: Joi.string(),
canceled_at: Joi.dateFilter(),
created_at: Joi.dateFilter(),
updated_at: Joi.dateFilter(),
})
}
export default Joi
@@ -11,9 +11,6 @@ describe("BaseService", () => {
id: "1234",
test1: ["123", "12", "1"],
test2: Not("this"),
rec: {
first: ["1", "2", "3"],
},
},
{
relations: ["1234"],
@@ -25,9 +22,6 @@ describe("BaseService", () => {
id: "1234",
test1: In(["123", "12", "1"]),
test2: Not("this"),
rec: {
first: In(["1", "2", "3"]),
},
},
relations: ["1234"],
})
+27 -2
View File
@@ -1,5 +1,5 @@
import { MedusaError } from "medusa-core-utils"
import { In, FindOperator, getManager } from "typeorm"
import { In, FindOperator, Raw } from "typeorm"
/**
* Common functionality for Services
@@ -29,7 +29,32 @@ class BaseService {
acc[key] = In([...value])
break
case value !== null && typeof value === "object":
acc[key] = build(value)
const subquery = []
Object.entries(value).map(([modifier, val]) => {
switch (modifier) {
case "lt":
subquery.push({ operator: "<", value: val })
break
case "gt":
subquery.push({ operator: ">", value: val })
break
case "lte":
subquery.push({ operator: "<=", value: val })
break
case "gte":
subquery.push({ operator: ">=", value: val })
break
}
})
acc[key] = Raw(
a =>
subquery
.map((s, index) => `${a} ${s.operator} :${index}`)
.join(" AND "),
subquery.map(s => s.value)
)
break
default:
acc[key] = value
@@ -1,9 +1,11 @@
import { default as authenticateCustomer } from "./authenticate-customer"
import { default as authenticate } from "./authenticate"
import { default as normalizeQuery } from "./normalized-query"
import { default as wrap } from "./await-middleware"
export default {
authenticate,
authenticateCustomer,
normalizeQuery,
wrap,
}
@@ -0,0 +1,16 @@
export default () => {
return (req, res, next) => {
const clean = Object.entries(req.query).reduce((acc, [key, val]) => {
if (Array.isArray(val) && val.length === 1) {
acc[key] = val[0].split(",")
} else {
acc[key] = val
}
return acc
}, {})
req.query = clean
next()
}
}
@@ -9,7 +9,11 @@ export default app => {
/**
* List orders
*/
route.get("/", middlewares.wrap(require("./list-orders").default))
route.get(
"/",
middlewares.normalizeQuery(),
middlewares.wrap(require("./list-orders").default)
)
/**
* Get an order
@@ -280,3 +284,20 @@ export const allowedRelations = [
"swaps.return_order",
"swaps.additional_items",
]
export const filterableFields = [
"id",
"status",
"fulfillment_status",
"payment_status",
"display_id",
"cart_id",
"customer_id",
"email",
"region_id",
"currency_code",
"tax_rate",
"canceled_at",
"created_at",
"updated_at",
]
@@ -1,8 +1,16 @@
import _ from "lodash"
import { Not } from "typeorm"
import { defaultRelations, defaultFields } from "./"
import { defaultRelations, defaultFields, filterableFields } from "./"
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const schema = Validator.orderFilter()
const { value, error } = schema.validate(req.query)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const orderService = req.scope.resolve("orderService")
@@ -18,6 +26,8 @@ export default async (req, res) => {
let includeFields = []
if ("fields" in req.query) {
includeFields = req.query.fields.split(",")
// Ensure created_at is included, since we are sorting on this
includeFields.push("created_at")
}
let expandFields = []
@@ -25,17 +35,9 @@ export default async (req, res) => {
expandFields = req.query.expand.split(",")
}
if ("new" in req.query) {
selector = {
payment_status: Not("captured"),
fulfillment_status: Not("shipped"),
}
}
if ("requires_more" in req.query) {
selector = {
payment_status: Not("captured"),
fulfillment_status: Not("shipped"),
for (const k of filterableFields) {
if (k in value) {
selector[k] = value[k]
}
}
@@ -52,8 +54,12 @@ export default async (req, res) => {
listConfig
)
let data = orders
const fields = [...includeFields, ...expandFields]
const data = orders.map(o => _.pick(o, fields))
if (fields.length) {
data = orders.map(o => _.pick(o, fields))
}
res.json({ orders: data, count, offset, limit })
} catch (error) {