Skip to content

Commit e81f606

Browse files
nabinkednthapa-atl
andauthoredOct 5, 2023
Handle additional OAS keywords (#141)
* log errors in strict mode * add changeset * fix typos * change changeset message * change title * update comment * remove unused line * add line between functions --------- Co-authored-by: nthapa <nthapa@atlassian.com>
1 parent 35ee143 commit e81f606

File tree

5 files changed

+122
-3
lines changed

5 files changed

+122
-3
lines changed
 

‎.changeset/eighty-falcons-refuse.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'oas3-chow-chow': patch
3+
---
4+
5+
handle additional open api keywords

‎__test__/chow-keywords.spec.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { OpenAPIObject } from 'openapi3-ts';
2+
import ChowChow from '../src';
3+
const doc: (additionalKeywords: Record<string, any>) => OpenAPIObject = (
4+
additionalKeywords = {}
5+
) => ({
6+
openapi: '3.0.1',
7+
info: {
8+
title: 'service open api spec',
9+
version: '1.0.1',
10+
},
11+
components: {
12+
schemas: {
13+
ResolveUnsupportedError: {
14+
type: 'object',
15+
properties: {
16+
error: {},
17+
},
18+
...additionalKeywords,
19+
},
20+
},
21+
},
22+
paths: {
23+
'/resolve': {
24+
post: {
25+
operationId: 'resolve',
26+
responses: {
27+
'404': {
28+
content: {
29+
'application/json': {
30+
schema: {
31+
$ref: '#/components/schemas/ResolveUnsupportedError',
32+
},
33+
},
34+
},
35+
},
36+
},
37+
},
38+
},
39+
},
40+
});
41+
42+
describe('additional open api keywords', () => {
43+
it.each([
44+
{ discriminator: '' },
45+
{ example: '' },
46+
{ externalDocs: '' },
47+
{ xml: '' },
48+
])('"%s" keyword should be allowed by default', async (additionalKeyword) => {
49+
expect(await ChowChow.create(doc(additionalKeyword))).toBeDefined();
50+
});
51+
});

‎__test__/chow-strict.spec.ts

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { OpenAPIObject, PathItemObject } from 'openapi3-ts';
2+
import ChowChow from '../src';
3+
4+
/**
5+
* https://ajv.js.org/strict-mode.html
6+
*/
7+
describe('strict mode', () => {
8+
it('show log warn and not throw by default', async () => {
9+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
10+
const doc: OpenAPIObject = {
11+
openapi: '3.0.1',
12+
info: {
13+
title: 'service open api spec',
14+
version: '1.0.1',
15+
},
16+
components: {
17+
schemas: {
18+
ResolveUnsupportedError: {
19+
type: 'array',
20+
additionalItems: false,
21+
},
22+
},
23+
},
24+
paths: {
25+
'/resolve': {
26+
post: {
27+
operationId: 'resolve',
28+
responses: {
29+
'404': {
30+
content: {
31+
'application/json': {
32+
schema: {
33+
$ref: '#/components/schemas/ResolveUnsupportedError',
34+
},
35+
},
36+
},
37+
},
38+
},
39+
},
40+
},
41+
},
42+
};
43+
expect(await ChowChow.create(doc)).toBeDefined();
44+
expect(warnSpy).toHaveBeenCalledWith(
45+
'strict mode: "additionalItems" is ignored when "items" is not an array of schemas'
46+
);
47+
warnSpy.mockRestore();
48+
});
49+
});

‎src/compiler/CompiledSchema.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,21 @@ export default class CompiledSchema {
88
private validator: Ajv.ValidateFunction;
99

1010
constructor(schema: SchemaObject, opts?: Ajv.Options, context?: any) {
11-
this.schemaObject = schema;
11+
/**
12+
* Remove unsupported additional OpenAPI keywords.
13+
* https://swagger.io/docs/specification/data-models/keywords/ See "Additional Keywords"
14+
* Does not include all keywords listed in that page/section because some of them are supported by ajv https://ajv.js.org/json-schema.html#openapi-support
15+
* and some are explictly added within this class.
16+
*/
17+
const {
18+
discriminator,
19+
example,
20+
externalDocs,
21+
xml,
22+
...schemaObject
23+
} = schema;
24+
this.schemaObject = schemaObject;
25+
1226
const ajvInstance = ajv(opts);
1327
ajvInstance.removeKeyword('writeOnly');
1428
ajvInstance.removeKeyword('readOnly');
@@ -22,7 +36,7 @@ export default class CompiledSchema {
2236
validate: (schema: any) =>
2337
schema ? context.schemaContext === 'response' : true,
2438
});
25-
this.validator = ajvInstance.compile(schema);
39+
this.validator = ajvInstance.compile(schemaObject);
2640
}
2741

2842
public validate(value: any) {

‎src/compiler/ajv.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Ajv, { Options } from 'ajv';
22
import addFormats from 'ajv-formats';
33

4-
const options: Options = {};
4+
const options: Options = { strict: 'log' };
55

66
export default function ajv(opts: Options = {}) {
77
const ajv = new Ajv({

0 commit comments

Comments
 (0)
Please sign in to comment.