Skip to content

Commit

Permalink
(#82) Use more explicit context for schema validation functions and a…
Browse files Browse the repository at this point in the history
…llow async validation functions
  • Loading branch information
predetermined authored Oct 12, 2021
2 parents 24bdcf7 + d2bcf6d commit 57eb316
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 27 deletions.
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

0 comments on commit 57eb316

Please sign in to comment.