Skip to content

Commit 1d7f9aa

Browse files
authored
fix(api): add missing plugins (#265)
* add missing plugins * style: resolve style guide violations --------- Co-authored-by: oXtxNt9U <[email protected]>
1 parent b0dc608 commit 1d7f9aa

File tree

4 files changed

+167
-0
lines changed

4 files changed

+167
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Hapi from "@hapi/hapi";
2+
3+
export const commaArrayQuery = {
4+
name: "comma-array-query",
5+
onRequest(request: Hapi.Request, h: Hapi.ResponseToolkit): Hapi.Lifecycle.ReturnValue {
6+
const query = {};
7+
const separator = ",";
8+
9+
for (const [key, value] of Object.entries(request.query as { [key: string]: string })) {
10+
query[key] = value.includes(separator) ? value.split(separator) : value;
11+
}
12+
13+
request.query = query;
14+
return h.continue;
15+
},
16+
17+
register(server: Hapi.Server): void {
18+
server.ext("onRequest", this.onRequest);
19+
},
20+
21+
version: "1.0.0",
22+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Hapi from "@hapi/hapi";
2+
import { set } from "@mainsail/utils";
3+
4+
export const dotSeparatedQuery = {
5+
name: "dot-separated-query",
6+
onRequest(request: Hapi.Request, h: Hapi.ResponseToolkit): Hapi.Lifecycle.ReturnValue {
7+
const query = {};
8+
for (const [key, value] of Object.entries(request.query)) {
9+
set(query, key, value);
10+
}
11+
request.query = query;
12+
return h.continue;
13+
},
14+
15+
register(server: Hapi.Server): void {
16+
server.ext("onRequest", this.onRequest);
17+
},
18+
19+
version: "1.0.0",
20+
};

packages/api-http/source/plugins/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { commaArrayQuery } from "./comma-separated-query";
2+
import { dotSeparatedQuery } from "./dot-separated-query";
13
import { hapiAjv } from "./hapi-ajv";
24
import { pagination } from "./pagination";
35
import { rateLimit } from "./rate-limit";
@@ -20,6 +22,8 @@ export const preparePlugins = (config) => [
2022
},
2123
plugin: rateLimit,
2224
},
25+
{ plugin: commaArrayQuery },
26+
{ plugin: dotSeparatedQuery },
2327
{
2428
options: {
2529
query: {
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import Joi from "joi";
2+
import { describe, Sandbox } from "../../../test-framework";
3+
import * as schemas from "./schemas";
4+
5+
describe<{}>("Schemas", ({ it, assert }) => {
6+
describe("createRangeCriteriaSchema", () => {
7+
it("should be valid", () => {
8+
const schema = schemas.createRangeCriteriaSchema(Joi.number().integer().min(1));
9+
10+
const result = schema.validate({ from: 1, to: 2 });
11+
12+
assert.equal(result, {
13+
value: {
14+
from: 1,
15+
to: 2,
16+
},
17+
});
18+
});
19+
20+
it("should be invalid if from doesn't satisfy condition", () => {
21+
const schema = schemas.createRangeCriteriaSchema(Joi.number().integer().min(1));
22+
23+
const result = schema.validate({ from: 0, to: 2 });
24+
25+
assert.equal(result.error!.message, '"from" must be greater than or equal to 1');
26+
});
27+
28+
it("should be invalid if to doesn't satisfy condition", () => {
29+
const schema = schemas.createRangeCriteriaSchema(Joi.number().integer().min(1));
30+
31+
const result = schema.validate({ from: 1, to: 0 });
32+
33+
assert.equal(result.error!.message, '"to" must be greater than or equal to 1');
34+
});
35+
});
36+
37+
describe("createSortingSchema", () => {
38+
const testCriteriaSchemaObject = {
39+
username: Joi.string().max(256),
40+
};
41+
42+
it("should use asc direction if direction is not present", () => {
43+
const sortingSchema = schemas.createSortingSchema(testCriteriaSchemaObject);
44+
45+
const result = sortingSchema.validate({ orderBy: "username" });
46+
47+
assert.equal(result, {
48+
value: {
49+
orderBy: [
50+
{
51+
property: "username",
52+
direction: "asc",
53+
},
54+
],
55+
},
56+
});
57+
});
58+
59+
it("should use given direction", () => {
60+
const sortingSchema = schemas.createSortingSchema(testCriteriaSchemaObject);
61+
62+
const result = sortingSchema.validate({ orderBy: "username:desc" });
63+
64+
assert.equal(result, {
65+
value: {
66+
orderBy: [
67+
{
68+
property: "username",
69+
direction: "desc",
70+
},
71+
],
72+
},
73+
});
74+
});
75+
76+
it("should return empty order if orderBy is empty string", () => {
77+
const sortingSchema = schemas.createSortingSchema(testCriteriaSchemaObject);
78+
79+
const result = sortingSchema.validate({ orderBy: "" });
80+
81+
assert.equal(result, {
82+
value: {
83+
orderBy: [],
84+
},
85+
});
86+
});
87+
88+
it("should contain error if direction is unknown", () => {
89+
const sortingSchema = schemas.createSortingSchema(testCriteriaSchemaObject);
90+
91+
const result = sortingSchema.validate({ orderBy: "username:invalid" });
92+
93+
assert.equal(result.error!.message, "Unexpected orderBy direction 'invalid' for property 'username'");
94+
});
95+
96+
it("should contain error if property is unknown", () => {
97+
const sortingSchema = schemas.createSortingSchema(testCriteriaSchemaObject);
98+
99+
const result = sortingSchema.validate({ orderBy: "invalid:asc" });
100+
101+
assert.equal(result.error!.message, "Unknown orderBy property 'invalid'");
102+
});
103+
104+
it("should return orderBy if property is defined in wildcardPaths", () => {
105+
const sortingSchema = schemas.createSortingSchema(testCriteriaSchemaObject, ["invalid"]);
106+
107+
const result = sortingSchema.validate({ orderBy: "invalid.username:asc" });
108+
109+
assert.equal(result, {
110+
value: {
111+
orderBy: [
112+
{
113+
property: "invalid.username",
114+
direction: "asc",
115+
},
116+
],
117+
},
118+
});
119+
});
120+
});
121+
});

0 commit comments

Comments
 (0)