Skip to content

Commit

Permalink
Use OpenAPI schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed Jan 15, 2020
1 parent ead745a commit 31fc677
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 35 deletions.
4 changes: 2 additions & 2 deletions src/functions/__tests__/schema.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { JSONSchema4, JSONSchema6 } from 'json-schema';
import { schema } from '../schema';

function runSchema(target: any, schemaObj: object) {
return schema(target, { schema: schemaObj }, { given: [] }, { given: null, original: null } as any);
function runSchema(target: any, schemaObj: object, oasVersion: number = 0) {
return schema(target, { schema: schemaObj, oasVersion }, { given: [] }, { given: null, original: null } as any);
}

describe('schema', () => {
Expand Down
4 changes: 3 additions & 1 deletion src/functions/schema-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface ISchemaPathOptions {
schemaPath: string;
// the `path.to.prop` to field, or special `@key` value to target keys for matched `given` object
field?: string;
// The oasVersion, either 2 or 3
oasVersion?: number;
}

export type SchemaPathRule = IRule<RuleFunction.SCHEMAPATH, ISchemaPathOptions>;
Expand Down Expand Up @@ -45,5 +47,5 @@ export const schemaPath: IFunction<ISchemaPathOptions> = (targetVal, opts, paths
}
}

return schema(relevantObject, { schema: schemaObject }, paths, otherValues);
return schema(relevantObject, { schema: schemaObject, oasVersion: opts.oasVersion }, paths, otherValues);
};
91 changes: 68 additions & 23 deletions src/functions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import * as jsonSpecv6 from 'ajv/lib/refs/json-schema-draft-06.json';
import * as jsonSpecv7 from 'ajv/lib/refs/json-schema-draft-07.json';
import { IOutputError } from 'better-ajv-errors';
import { escapeRegExp } from 'lodash';
import * as oasSpec2 from '../rulesets/oas/schemas/schema.oas2.json';
import * as oasSpec3 from '../rulesets/oas/schemas/schema.oas3.json';
import { IFunction, IFunctionResult, IRule, JSONSchema, RuleFunction } from '../types';
const oasFormatValidator = require('ajv-oai/lib/format-validator');
const betterAjvErrors = require('better-ajv-errors/lib/modern');

export interface ISchemaOptions {
schema: object;
// The oasVersion, either 2 or 3
oasVersion?: number;
}

export type SchemaRule = IRule<RuleFunction.SCHEMA, ISchemaOptions>;
Expand All @@ -33,27 +37,67 @@ const logger = {
error: console.error,
};

const ajv = new AJV({
meta: false,
schemaId: 'auto',
jsonPointers: true,
unknownFormats: 'ignore',
nullable: true,
logger,
});
ajv.addMetaSchema(jsonSpecv4);
ajv.addMetaSchema(jsonSpecv6);
ajv.addMetaSchema(jsonSpecv7);
// @ts-ignore
ajv._opts.defaultMeta = jsonSpecv4.id;
// @ts-ignore
ajv._refs['http://json-schema.org/schema'] = 'http://json-schema.org/draft-04/schema';

ajv.addFormat('int32', { type: 'number', validate: oasFormatValidator.int32 });
ajv.addFormat('int64', { type: 'number', validate: oasFormatValidator.int64 });
ajv.addFormat('float', { type: 'number', validate: oasFormatValidator.float });
ajv.addFormat('double', { type: 'number', validate: oasFormatValidator.double });
ajv.addFormat('byte', { type: 'string', validate: oasFormatValidator.byte });
const ajvInstances = {};

function getAjv(oasVersion: number = 0): AJV.Ajv {
if (typeof ajvInstances[oasVersion] !== 'undefined') {
return ajvInstances[oasVersion];
}

const ajvOpts: object = {
meta: false,
schemaId: 'auto',
jsonPointers: true,
unknownFormats: 'ignore',
logger,
};
const ajv = new AJV(ajvOpts);
ajv.addMetaSchema(jsonSpecv4);

let defaultMeta;
if (oasVersion >= 2 && oasVersion < 4) {
let oasSpec;
let schemaPath;
if (oasVersion === 2) {
oasSpec = oasSpec2;
schemaPath = '#/definitions/schema';
} else {
oasSpec = oasSpec3;
schemaPath = '#/definitions/Schema';
}

ajv.addMetaSchema(oasSpec);
const oasSchemaType = {
id: 'https://stoplight.io/oas/' + oasVersion + '/schemaType',
$schema: 'http://json-schema.org/draft-04/schema#',
allOf: [
{
$ref: oasSpec.id + schemaPath,
},
],
};
ajv.addMetaSchema(oasSchemaType);
defaultMeta = oasSchemaType.id;
} else {
// Backward compatibility: For all other cases than OAS2 or 3
ajv.addMetaSchema(jsonSpecv6);
ajv.addMetaSchema(jsonSpecv7);
defaultMeta = jsonSpecv4.id;
}

// @ts-ignore
ajv._opts.defaultMeta = defaultMeta;
// @ts-ignore
ajv._refs['http://json-schema.org/schema'] = 'http://json-schema.org/draft-04/schema';

ajv.addFormat('int32', { type: 'number', validate: oasFormatValidator.int32 });
ajv.addFormat('int64', { type: 'number', validate: oasFormatValidator.int64 });
ajv.addFormat('float', { type: 'number', validate: oasFormatValidator.float });
ajv.addFormat('double', { type: 'number', validate: oasFormatValidator.double });
ajv.addFormat('byte', { type: 'string', validate: oasFormatValidator.byte });

return ajv;
}

function getSchemaId(schemaObj: JSONSchema): void | string {
if ('$id' in schemaObj) {
Expand All @@ -66,7 +110,8 @@ function getSchemaId(schemaObj: JSONSchema): void | string {
}

const validators = new (class extends WeakMap<JSONSchema, ValidateFunction> {
public get(schemaObj: JSONSchema) {
public get(schemaObj: JSONSchema, oasVersion?: number) {
const ajv = getAjv(oasVersion);
const schemaId = getSchemaId(schemaObj);
let validator = schemaId !== void 0 ? ajv.getSchema(schemaId) : void 0;
if (validator !== void 0) {
Expand Down Expand Up @@ -129,7 +174,7 @@ export const schema: IFunction<ISchemaOptions> = (targetVal, opts, paths) => {

try {
// we used the compiled validation now, hence this lookup here (see the logic above for more info)
const validator = validators.get(schemaObj);
const validator = validators.get(schemaObj, opts.oasVersion);
if (!validator(targetVal) && validator.errors) {
try {
results.push(
Expand Down
27 changes: 18 additions & 9 deletions src/rulesets/oas/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$"
"schemaPath": "$",
"oasVersion": 2
}
}
},
Expand All @@ -547,7 +548,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$"
"schemaPath": "$",
"oasVersion": 2
}
}
},
Expand Down Expand Up @@ -708,7 +710,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$.schema"
"schemaPath": "$.schema",
"oasVersion": 3
}
}
},
Expand All @@ -724,7 +727,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$.schema"
"schemaPath": "$.schema",
"oasVersion": 3
}
}
},
Expand All @@ -740,7 +744,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$.schema"
"schemaPath": "$.schema",
"oasVersion": 3
}
}
},
Expand All @@ -756,7 +761,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$"
"schemaPath": "$",
"oasVersion": 3
}
}
},
Expand All @@ -772,7 +778,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$"
"schemaPath": "$",
"oasVersion": 3
}
}
},
Expand All @@ -788,7 +795,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$"
"schemaPath": "$",
"oasVersion": 3
}
}
},
Expand All @@ -804,7 +812,8 @@
"function": "schemaPath",
"functionOptions": {
"field": "example",
"schemaPath": "$"
"schemaPath": "$",
"oasVersion": 3
}
}
},
Expand Down

0 comments on commit 31fc677

Please sign in to comment.