UNPKG

2.51 kBPlain TextView Raw
1import { identity, pick, fromPairs } from 'lodash';
2import { Ajv, ValidateFunction } from 'ajv';
3import AjvCtor = require('ajv');
4
5export class ValidationError extends Error {
6 public readonly name = 'ValidationError';
7 constructor(message: string, public errors: any) {
8 super(message);
9 }
10}
11
12export interface ClassValidator {
13 [method: string]: ValidateFunction;
14}
15
16function createValidator(): Ajv {
17 const ajv = new AjvCtor({ useDefaults: true, allErrors: true });
18 ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
19 ajv.addKeyword('coerce-date', {
20 type: 'string',
21 modifying: true,
22 valid: true,
23 compile: (onOrOff: boolean, parentSchema: any) => {
24 if (parentSchema.format !== 'date-time') {
25 throw new Error('Format should be date-time when using coerce-date');
26 }
27 if (onOrOff !== true) {
28 return identity;
29 }
30 return (v: any, _dataPath?: string, obj?: object | any[], key?: string | number) => {
31 if (obj === undefined || key === undefined) {
32 throw new Error('Cannot coerce a date at root level');
33 }
34 (obj as any)[key] = new Date(v);
35 return true;
36 };
37 },
38 });
39 return ajv;
40}
41
42// tslint:disable:max-line-length
43/**
44 * Copied from https://github.com/WHenderson/json-pointer-rfc6901/blob/ca2bfd17abe37ff09394222128192023dbfb067b/src/json-pointer.coffee#L255
45 */
46function escapeJsonPointer(ptr: string) {
47 return encodeURIComponent(ptr.replace(/~/g, '~0').replace(/\//g, '~1'));
48}
49// tslint:enable:max-line-length
50
51export function createClassValidator(
52 schema: { definitions: { [key: string]: any } },
53 className: string,
54 field: string
55): ClassValidator {
56 const ajv = createValidator();
57 for (const [k, v] of Object.entries(schema.definitions)) {
58 ajv.addSchema(v, `#/definitions/${escapeJsonPointer(k)}`);
59 }
60 return fromPairs(Object.entries(schema.definitions[className].properties).map(([method, s]) => [
61 method, ajv.compile((s as any).properties[field]),
62 ]));
63}
64
65export function createReturnTypeValidator(
66 schema: { definitions: { [key: string]: any } },
67 className: string
68): ClassValidator {
69 const ajv = createValidator();
70 for (const [k, v] of Object.entries(schema.definitions)) {
71 ajv.addSchema(v, `#/definitions/${escapeJsonPointer(k)}`);
72 }
73 return fromPairs(Object.entries(schema.definitions[className].properties).map(([method, s]) => [
74 method, ajv.compile({ properties: pick((s as any).properties, 'returns') }),
75 ]));
76}