Skip to content

Commit 8b66994

Browse files
committed
Add OpenAPI 3.2 support
1 parent 8dcd391 commit 8b66994

5 files changed

Lines changed: 157 additions & 4 deletions

File tree

lib/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ const util = require("./util");
1111
const Options = require("./options");
1212
const maybe = require("call-me-maybe");
1313

14+
const supported32Versions = ["3.2.0"];
1415
const supported31Versions = ["3.1.0", "3.1.1", "3.1.2"];
1516
const supported30Versions = ["3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4"];
16-
const supportedVersions = [...supported31Versions, ...supported30Versions];
17+
const supportedVersions = [...supported32Versions, ...supported31Versions, ...supported30Versions];
1718

1819
/**
1920
* This class parses a Swagger 2.0 or 3.0 API, resolves its JSON references and their resolved values,
@@ -53,7 +54,7 @@ class SwaggerParser extends $RefParser {
5354
}
5455
} else {
5556
if (schema.paths === undefined) {
56-
if (supported31Versions.indexOf(schema.openapi) !== -1) {
57+
if (supported32Versions.indexOf(schema.openapi) !== -1 || supported31Versions.indexOf(schema.openapi) !== -1) {
5758
if (schema.webhooks === undefined) {
5859
throw new SyntaxError(`${args.path || args.schema} is not a valid Openapi API definition`);
5960
}

lib/validators/schema.js

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ function validateSchema(api) {
2121
schema = openapi.v2;
2222
ajv = initializeAjv();
2323
} else {
24-
if (api.openapi.startsWith("3.1")) {
25-
schema = openapi.v31;
24+
if (api.openapi.startsWith("3.1") || api.openapi.startsWith("3.2")) {
25+
schema = structuredClone(openapi.v31);
26+
27+
if (api.openapi.startsWith("3.2")) {
28+
applyOpenApi32Compat(schema);
29+
}
2630

2731
// There's a bug with Ajv in how it handles `$dynamicRef` in the way that it's used within the 3.1 schema so we
2832
// need to do some adhoc workarounds.
@@ -34,6 +38,9 @@ function validateSchema(api) {
3438
schema.$defs.components.properties.schemas.additionalProperties = schemaDynamicRef;
3539
schema.$defs.header.dependentSchemas.schema.properties.schema = schemaDynamicRef;
3640
schema.$defs["media-type"].properties.schema = schemaDynamicRef;
41+
if (schema.$defs["media-type"].properties.itemSchema) {
42+
schema.$defs["media-type"].properties.itemSchema = schemaDynamicRef;
43+
}
3744
schema.$defs.parameter.properties.schema = schemaDynamicRef;
3845

3946
ajv = initializeAjv(false);
@@ -54,6 +61,94 @@ function validateSchema(api) {
5461
}
5562
}
5663

64+
/**
65+
* Applies a targeted compatibility layer so the bundled OpenAPI 3.1 schema can validate
66+
* the most important OpenAPI 3.2 additions until @apidevtools/openapi-schemas ships v3.2.
67+
*
68+
* @param {object} schema
69+
*/
70+
function applyOpenApi32Compat(schema) {
71+
schema.properties.openapi.pattern = "^3\\.2\\.\\d+(-.+)?$";
72+
73+
schema.$defs.components.properties.mediaTypes = {
74+
type: "object",
75+
additionalProperties: {
76+
$ref: "#/$defs/media-type",
77+
},
78+
};
79+
80+
schema.$defs["path-item"].patternProperties["^query$"] = {
81+
$ref: "#/$defs/operation",
82+
};
83+
schema.$defs["path-item"].properties.additionalOperations = {
84+
type: "object",
85+
additionalProperties: {
86+
$ref: "#/$defs/operation",
87+
},
88+
};
89+
90+
schema.$defs.response.properties.summary = {
91+
type: "string",
92+
};
93+
94+
schema.$defs["media-type"].properties.itemSchema = {
95+
$dynamicRef: "#meta",
96+
};
97+
schema.$defs["media-type"].properties.prefixEncoding = {
98+
type: "array",
99+
items: {
100+
$ref: "#/$defs/encoding",
101+
},
102+
};
103+
schema.$defs["media-type"].properties.itemEncoding = {
104+
$ref: "#/$defs/encoding",
105+
};
106+
107+
schema.$defs.tag.properties.summary = {
108+
type: "string",
109+
};
110+
schema.$defs.tag.properties.parent = {
111+
type: "string",
112+
};
113+
schema.$defs.tag.properties.kind = {
114+
type: "string",
115+
};
116+
117+
schema.$defs.parameter.properties.in.enum.push("querystring");
118+
119+
schema.$defs["security-scheme"].properties.oauth2MetadataUrl = {
120+
$ref: "#/$defs/uri",
121+
};
122+
schema.$defs["security-scheme"].properties.deprecated = {
123+
default: false,
124+
type: "boolean",
125+
};
126+
127+
schema.$defs["oauth-flows"].properties.deviceAuthorization = {
128+
$ref: "#/$defs/oauth-flows/$defs/device-authorization",
129+
};
130+
schema.$defs["oauth-flows"].$defs["device-authorization"] = {
131+
type: "object",
132+
properties: {
133+
deviceAuthorizationUrl: {
134+
type: "string",
135+
},
136+
tokenUrl: {
137+
type: "string",
138+
},
139+
refreshUrl: {
140+
type: "string",
141+
},
142+
scopes: {
143+
$ref: "#/$defs/map-of-strings",
144+
},
145+
},
146+
required: ["deviceAuthorizationUrl", "tokenUrl", "scopes"],
147+
$ref: "#/$defs/specification-extensions",
148+
unevaluatedProperties: false,
149+
};
150+
}
151+
57152
/**
58153
* Determines which version of Ajv to load and prepares it for use.
59154
*

test/specs/invalid/invalid.spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,18 @@ describe("Invalid APIs (can't be parsed)", () => {
6565
expect(err.message).to.equal('API version number must be a string (e.g. "1.0.0") not a number.');
6666
}
6767
});
68+
69+
it("supports OpenAPI 3.2 parsing", async () => {
70+
const api = await SwaggerParser.parse({
71+
openapi: "3.2.0",
72+
info: {
73+
title: "Test API",
74+
version: "1.0.0",
75+
},
76+
paths: {},
77+
});
78+
79+
expect(api).to.be.an("object");
80+
expect(api.openapi).to.equal("3.2.0");
81+
});
6882
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
openapi: "3.2.0"
2+
info:
3+
title: OpenAPI 3.2 test
4+
version: "1.0.0"
5+
tags:
6+
- name: products
7+
summary: Products
8+
kind: nav
9+
components:
10+
mediaTypes:
11+
EventStream:
12+
itemSchema:
13+
type: string
14+
securitySchemes:
15+
oauth:
16+
type: oauth2
17+
oauth2MetadataUrl: https://example.com/.well-known/oauth-authorization-server
18+
flows:
19+
deviceAuthorization:
20+
deviceAuthorizationUrl: https://example.com/oauth/device
21+
tokenUrl: https://example.com/oauth/token
22+
scopes: {}
23+
paths:
24+
/events:
25+
query:
26+
responses:
27+
"200":
28+
description: ok
29+
summary: success
30+
content:
31+
application/jsonl:
32+
itemSchema:
33+
type: string
34+
additionalOperations:
35+
COPY:
36+
responses:
37+
"200":
38+
description: copied

test/specs/validate-schema/validate-schema.spec.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ describe("Invalid APIs (Swagger 2.0 schema validation)", () => {
9393
valid: true,
9494
file: "allof.yaml",
9595
},
96+
{
97+
name: "OpenAPI 3.2 document",
98+
valid: true,
99+
file: "openapi-3.2.yaml",
100+
},
96101
{
97102
name: 'Schema with "anyOf"',
98103
valid: false,

0 commit comments

Comments
 (0)