Skip to content

Commit

Permalink
feat: do not require koa as dependency (#13)
Browse files Browse the repository at this point in the history
This lets you publish libraries that validate koa requests, but don't depend on koa.
  • Loading branch information
Forbes Lindesay authored Jun 11, 2019
1 parent 01c4207 commit 670735f
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 275 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"test": "jest --no-cache",
"test:coverage": "yarn test --coverage",
"test:watch": "yarn test --watch",
"posttest": "tsc --noEmit",
"clean": "rimraf lib && rimraf src/Example.validator.ts",
"prebuild": "yarn clean",
"build": "tsc",
Expand Down Expand Up @@ -68,7 +69,7 @@
"json-stable-stringify": "^1.0.1",
"minimatch": "^3.0.4",
"tsconfig": "^7.0.0",
"typescript-json-schema": "^0.38.0",
"typescript-json-schema": "^0.38.3",
"yargs": "^13.2.4"
}
}
5 changes: 5 additions & 0 deletions src/__tests__/output/ComplexExample.usage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Context} from 'koa';
import {validateKoaRequest, RequestA} from './ComplexExample.validator';

declare const x: Context;
export const y: RequestA = validateKoaRequest('RequestA')(x);
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import Ajv = require('ajv');
import {Context} from 'koa';
import {inspect} from 'util';
import {MyEnum, TypeA, TypeB, RequestA, RequestB} from '../../ComplexExample';
import {inspect} from 'util';
export interface KoaContext {
readonly request?: unknown; // {body?: unknown}
readonly params?: unknown;
readonly query?: unknown;
throw(status: 400, message: string): unknown;
}
export const ajv = new Ajv({allErrors: true, coerceTypes: false});

ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
Expand Down Expand Up @@ -75,7 +80,7 @@ ajv.addSchema(Schema, 'Schema');
export function validateKoaRequest(
typeName: 'RequestA',
): (
ctx: Context,
ctx: KoaContext,
) => {
params: RequestA['params'];
query: RequestA['query'];
Expand All @@ -84,7 +89,7 @@ export function validateKoaRequest(
export function validateKoaRequest(
typeName: 'RequestB',
): (
ctx: Context,
ctx: KoaContext,
) => {
params: unknown;
query: RequestB['query'];
Expand All @@ -93,7 +98,7 @@ export function validateKoaRequest(
export function validateKoaRequest(
typeName: string,
): (
ctx: Context,
ctx: KoaContext,
) => {
params: unknown;
query: unknown;
Expand All @@ -102,7 +107,7 @@ export function validateKoaRequest(
export function validateKoaRequest(
typeName: string,
): (
ctx: Context,
ctx: KoaContext,
) => {
params: any;
query: any;
Expand All @@ -118,7 +123,7 @@ export function validateKoaRequest(
const validateProperty = (
prop: string,
validator: any,
ctx: Context,
ctx: KoaContext,
): any => {
const data =
prop === 'body'
Expand All @@ -133,7 +138,11 @@ export function validateKoaRequest(
'Invalid request: ' +
ajv.errorsText(validator.errors, {dataVar: prop}) +
'\n\n' +
inspect({params: ctx.params, query: ctx.query, body: ctx.body}),
inspect({
params: ctx.params,
query: ctx.query,
body: ctx.request && (ctx.request as any).body,
}),
);
}
}
Expand Down
19 changes: 15 additions & 4 deletions src/__tests__/printValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,29 @@ import {printTypeCollectionValidator} from '../printValidator';
import {writeFileSync} from 'fs';
import prettierFile from '../prettierFile';

let validate: typeof import('./output/ComplexExample.valiator') = undefined as any;
let validate: typeof import('./output/ComplexExample.validator') = undefined as any;

test('print', () => {
const {symbols, schema} = parse([
__dirname + '/../ComplexExample.ts',
]).getAllTypes();
writeFileSync(
__dirname + '/output/ComplexExample.valiator.ts',
__dirname + '/output/ComplexExample.validator.ts',
printTypeCollectionValidator(symbols, schema, '../../ComplexExample'),
);
prettierFile(__dirname + '/output/ComplexExample.valiator.ts');
validate = require('./output/ComplexExample.valiator');
prettierFile(__dirname + '/output/ComplexExample.validator.ts');
writeFileSync(
__dirname + '/output/ComplexExample.usage.ts',
`
import {Context} from 'koa';
import {validateKoaRequest, RequestA} from './ComplexExample.validator';
declare const x: Context;
export const y: RequestA = validateKoaRequest('RequestA')(x);
`,
);
prettierFile(__dirname + '/output/ComplexExample.usage.ts');
validate = require('./output/ComplexExample.validator');
});
test('validateValue', () => {
expect(validate.validate('MyEnum')(0)).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion src/printValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export function printTypeCollectionValidator(
});
return [
t.IMPORT_AJV,
...(koaTypes.length ? [t.IMPORT_KOA_CONTEXT, t.IMPORT_INSPECT] : []),
t.importNamedTypes(symbols, relativePath),
...(koaTypes.length ? [t.IMPORT_INSPECT, t.DECLARE_KOA_CONTEXT] : []),
t.declareAJV(options),
t.exportNamed(symbols),
t.declareSchema('Schema', schema),
Expand Down
18 changes: 12 additions & 6 deletions src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ export type GENERATED_COMMENT = `// generated by typescript-json-validator`;

export const IMPORT_INSPECT = `import {inspect} from 'util';`;
export const IMPORT_AJV = `import Ajv = require('ajv');`;
export const IMPORT_KOA_CONTEXT = `import {Context} from 'koa';`;

export const DECLARE_KOA_CONTEXT = `export interface KoaContext {
readonly request?: unknown; // {body?: unknown}
readonly params?: unknown;
readonly query?: unknown;
throw(status: 400, message: string): unknown;
}`;

export const importNamedTypes = (names: string[], relativePath: string) =>
`import {${names.join(', ')}} from '${relativePath}';`;
Expand Down Expand Up @@ -78,19 +84,19 @@ export const validateKoaRequestOverload = (
typeName: string,
schema: TJS.Definition,
) =>
`export function validateKoaRequest(typeName: '${typeName}'): (ctx: Context) => {
`export function validateKoaRequest(typeName: '${typeName}'): (ctx: KoaContext) => {
params: ${typeOf(typeName, 'params', schema)},
query: ${typeOf(typeName, 'query', schema)},
body: ${typeOf(typeName, 'body', schema)},
};`;

export const VALIDATE_KOA_REQUEST_FALLBACK = `export function validateKoaRequest(typeName: string): (ctx: Context) => {
export const VALIDATE_KOA_REQUEST_FALLBACK = `export function validateKoaRequest(typeName: string): (ctx: KoaContext) => {
params: unknown,
query: unknown,
body: unknown,
};`;

export const VALIDATE_KOA_REQUEST_IMPLEMENTATION = `export function validateKoaRequest(typeName: string): (ctx: Context) => {
export const VALIDATE_KOA_REQUEST_IMPLEMENTATION = `export function validateKoaRequest(typeName: string): (ctx: KoaContext) => {
params: any,
query: any,
body: any,
Expand All @@ -101,7 +107,7 @@ export const VALIDATE_KOA_REQUEST_IMPLEMENTATION = `export function validateKoaR
const validateProperty = (
prop: string,
validator: any,
ctx: Context,
ctx: KoaContext,
): any => {
const data = prop === 'body' ? ctx.request && (ctx.request as any).body : (ctx as any)[prop];
if (validator) {
Expand All @@ -110,7 +116,7 @@ export const VALIDATE_KOA_REQUEST_IMPLEMENTATION = `export function validateKoaR
if (!valid) {
ctx.throw(
400,
'Invalid request: ' + ajv.errorsText(validator.errors, {dataVar: prop}) + '\\n\\n' + inspect({params: ctx.params, query: ctx.query, body: ctx.body}),
'Invalid request: ' + ajv.errorsText(validator.errors, {dataVar: prop}) + '\\n\\n' + inspect({params: ctx.params, query: ctx.query, body: ctx.request && (ctx.request as any).body}),
);
}
}
Expand Down
Loading

0 comments on commit 670735f

Please sign in to comment.