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

Use more explicit context for schema validation functions and allow async validation functions #82

Merged
merged 3 commits into from
Oct 12, 2021
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ app.get("/", (req) => {
schema: {
query: [
mustExist("hello"),
(query) => query["hello"] !== "world",
],
},
});
```

This schema would only allow requests with the `hello` query (for example,
`GET /?hello=yes`). The following helper functions are currently available:
This schema would only allow requests with the `hello` query present and the value not being `"world"`
(for example, `GET /?hello=yes`). The following helper functions are currently available:

- `mustExist(key)`
- `valueMustBeOfType(key, type)`
Expand Down
35 changes: 16 additions & 19 deletions aqua.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,10 @@ export interface Options {
};
}

export type RoutingSchemaValidationFunction = (
this: RoutingSchemaValidationContext,
context: RoutingSchemaValidationContext,
) => boolean;

type BroadestSchemaValidationContextValueTypes = Json;

type RoutingSchemaValidationContext = Record<
string,
BroadestSchemaValidationContextValueTypes
>;
export type RoutingSchemaValidationFunction<Context> = (
this: Context,
context: Context,
) => boolean | Promise<boolean>;

type RoutingSchemaKeys =
| "body"
Expand All @@ -119,7 +112,9 @@ type RoutingSchemaKeys =
| "headers";

type RoutingSchema = {
[requestKey in RoutingSchemaKeys]?: RoutingSchemaValidationFunction[];
[requestKey in RoutingSchemaKeys]?: RoutingSchemaValidationFunction<
Request[requestKey]
>[];
};

export interface RoutingOptions {
Expand All @@ -131,7 +126,9 @@ export enum MiddlewareType {
Outgoing = "Outgoing",
}

export function mustExist(key: string): RoutingSchemaValidationFunction {
export function mustExist(
key: string,
): RoutingSchemaValidationFunction<Record<string, unknown>> {
return function () {
return Object.keys(this).includes(key);
};
Expand All @@ -140,16 +137,16 @@ export function mustExist(key: string): RoutingSchemaValidationFunction {
export function valueMustBeOfType(
key: string,
type: "string" | "number" | "boolean" | "object" | "undefined",
): RoutingSchemaValidationFunction {
): RoutingSchemaValidationFunction<Record<string, unknown>> {
return function () {
return Object.keys(this).includes(key) && typeof this[key] === type;
};
}

export function mustContainValue(
key: string,
values: BroadestSchemaValidationContextValueTypes[],
): RoutingSchemaValidationFunction {
values: unknown[],
): RoutingSchemaValidationFunction<Record<string, unknown>> {
return function () {
return Object.keys(this).includes(key) && values.includes(this[key]);
};
Expand Down Expand Up @@ -310,12 +307,12 @@ export default class Aqua {
) as RoutingSchemaKeys[]
) {
for (
const validationFunction of route.options.schema[
const validationFunction of (route.options.schema[
routingSchemaKey
] || []
] || []) as RoutingSchemaValidationFunction<unknown>[]
) {
const schemaContext = req[routingSchemaKey];
if (!validationFunction.bind(schemaContext)(schemaContext)) {
if (!(await validationFunction.bind(schemaContext)(schemaContext))) {
passedAllValidations = false;
break routingSchemaIterator;
}
Expand Down
58 changes: 52 additions & 6 deletions tests/uptests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ registerTest(

const content = await requestContent(`/api/v2/hello/world/i`);
if (content !== "Not found.") {
throw Error(
"URL parameters don't seem to work",
);
throw Error("URL parameters don't seem to work");
}
},
);
Expand All @@ -125,9 +123,7 @@ registerTest(

const content = await requestContent(`/api3/hello/test`);
if (content === "matched") {
throw Error(
"URL parameters slash positioning caused an error",
);
throw Error("URL parameters slash positioning caused an error");
}
},
);
Expand Down Expand Up @@ -272,6 +268,56 @@ registerTest("Parameter schemas working?", async () => {
}
});

registerTest("Async schema validation functions passes?", async () => {
app.get(
"/test-parameter-schema-working-async/:hello",
(_req) => "Hello, World!",
{
schema: {
parameters: [
async (parameters) => {
return await new Promise((r) => r(!!parameters["hello"]));
},
],
},
},
);

const content = await requestContent(
"/test-parameter-schema-working-async/test",
);
if (content !== "Hello, World!") {
throw Error(
"Async schema validation function didn't pass although it shouldn",
);
}
});

registerTest("Async schema validation functions fails?", async () => {
app.get(
"/test-parameter-schema-working-async-fail/:hello",
(_req) => "Hello, World!",
{
schema: {
parameters: [
async (parameters) => {
return await new Promise((r) => r(!!parameters["notfound"]));
},
],
},
},
);

const content = await requestContent(
"/test-parameter-schema-working-async-fail/test",
);
if (content === "Hello, World!") {
throw Error(
"Async schema validation function passed although it shouldn't",
);
}
});

registerTest(
"Parameter schemas failing if validation functions should return false provided?",
async () => {
Expand Down