docusaurus-plugin-openapi-docs 1.0.6 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/lib/markdown/createSchemaDetails.js +325 -132
- package/lib/markdown/index.js +1 -0
- package/lib/markdown/schema.js +25 -9
- package/lib/markdown/utils.d.ts +1 -1
- package/lib/markdown/utils.js +4 -1
- package/lib/openapi/openapi.d.ts +3 -3
- package/lib/openapi/openapi.js +30 -26
- package/lib/openapi/types.d.ts +2 -1
- package/lib/openapi/utils/loadAndResolveSpec.d.ts +2 -0
- package/lib/openapi/utils/{loadAndBundleSpec.js → loadAndResolveSpec.js} +61 -28
- package/lib/openapi/utils/services/OpenAPIParser.d.ts +52 -0
- package/lib/openapi/utils/services/OpenAPIParser.js +342 -0
- package/lib/openapi/utils/services/RedocNormalizedOptions.d.ts +100 -0
- package/lib/openapi/utils/services/RedocNormalizedOptions.js +170 -0
- package/lib/openapi/utils/types/index.d.ts +2 -0
- package/lib/openapi/utils/types/index.js +23 -0
- package/lib/openapi/utils/types/open-api.d.ts +305 -0
- package/lib/openapi/utils/types/open-api.js +8 -0
- package/lib/openapi/utils/utils/JsonPointer.d.ts +51 -0
- package/lib/openapi/utils/utils/JsonPointer.js +95 -0
- package/lib/openapi/utils/utils/helpers.d.ts +43 -0
- package/lib/openapi/utils/utils/helpers.js +230 -0
- package/lib/openapi/utils/utils/index.d.ts +3 -0
- package/lib/openapi/utils/utils/index.js +25 -0
- package/lib/openapi/utils/utils/openapi.d.ts +40 -0
- package/lib/openapi/utils/utils/openapi.js +605 -0
- package/lib/sidebars/index.js +5 -3
- package/package.json +15 -11
- package/src/markdown/createSchemaDetails.ts +405 -159
- package/src/markdown/index.ts +1 -0
- package/src/markdown/schema.ts +28 -8
- package/src/markdown/utils.ts +5 -2
- package/src/openapi/openapi.ts +42 -38
- package/src/openapi/types.ts +2 -1
- package/src/openapi/utils/loadAndResolveSpec.ts +123 -0
- package/src/openapi/utils/services/OpenAPIParser.ts +433 -0
- package/src/openapi/utils/services/RedocNormalizedOptions.ts +330 -0
- package/src/openapi/utils/types/index.ts +10 -0
- package/src/openapi/utils/types/open-api.ts +303 -0
- package/src/openapi/utils/utils/JsonPointer.ts +99 -0
- package/src/openapi/utils/utils/helpers.ts +239 -0
- package/src/openapi/utils/utils/index.ts +11 -0
- package/src/openapi/utils/utils/openapi.ts +771 -0
- package/src/sidebars/index.ts +7 -4
- package/lib/openapi/utils/loadAndBundleSpec.d.ts +0 -3
- package/src/openapi/utils/loadAndBundleSpec.ts +0 -93
package/lib/openapi/openapi.js
CHANGED
|
@@ -16,18 +16,10 @@ const openapi_to_postmanv2_1 = __importDefault(require("@paloaltonetworks/openap
|
|
|
16
16
|
const postman_collection_1 = __importDefault(require("@paloaltonetworks/postman-collection"));
|
|
17
17
|
const chalk_1 = __importDefault(require("chalk"));
|
|
18
18
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
19
|
-
const json_refs_1 = __importDefault(require("json-refs"));
|
|
20
19
|
const lodash_1 = require("lodash");
|
|
21
20
|
const index_1 = require("../index");
|
|
22
21
|
const createExample_1 = require("./createExample");
|
|
23
|
-
const
|
|
24
|
-
/**
|
|
25
|
-
* Finds any reference objects in the OpenAPI definition and resolves them to a finalized value.
|
|
26
|
-
*/
|
|
27
|
-
async function resolveRefs(openapiData) {
|
|
28
|
-
const { resolved } = await json_refs_1.default.resolveRefs(openapiData);
|
|
29
|
-
return resolved;
|
|
30
|
-
}
|
|
22
|
+
const loadAndResolveSpec_1 = require("./utils/loadAndResolveSpec");
|
|
31
23
|
/**
|
|
32
24
|
* Convenience function for converting raw JSON to a Postman Collection object.
|
|
33
25
|
*/
|
|
@@ -48,7 +40,8 @@ function jsonToCollection(data) {
|
|
|
48
40
|
*/
|
|
49
41
|
async function createPostmanCollection(openapiData) {
|
|
50
42
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
51
|
-
|
|
43
|
+
// Create copy of openapiData
|
|
44
|
+
const data = Object.assign({}, openapiData);
|
|
52
45
|
// Including `servers` breaks postman, so delete all of them.
|
|
53
46
|
delete data.servers;
|
|
54
47
|
for (let pathItemObject of Object.values(data.paths)) {
|
|
@@ -122,7 +115,9 @@ function createItems(openapiData, sidebarOptions) {
|
|
|
122
115
|
operationObject.description =
|
|
123
116
|
(_h = (_g = operationObject.summary) !== null && _g !== void 0 ? _g : operationObject.operationId) !== null && _h !== void 0 ? _h : "";
|
|
124
117
|
}
|
|
125
|
-
const baseId =
|
|
118
|
+
const baseId = operationObject.operationId
|
|
119
|
+
? (0, lodash_1.kebabCase)(operationObject.operationId)
|
|
120
|
+
: (0, lodash_1.kebabCase)(operationObject.summary);
|
|
126
121
|
const servers = (_k = (_j = operationObject.servers) !== null && _j !== void 0 ? _j : pathObject.servers) !== null && _k !== void 0 ? _k : openapiData.servers;
|
|
127
122
|
const security = (_l = operationObject.security) !== null && _l !== void 0 ? _l : openapiData.security;
|
|
128
123
|
// Add security schemes so we know how to handle security.
|
|
@@ -189,9 +184,6 @@ function bindCollectionToApiItems(items, postmanCollection) {
|
|
|
189
184
|
});
|
|
190
185
|
}
|
|
191
186
|
async function readOpenapiFiles(openapiPath, options) {
|
|
192
|
-
// TODO: determine if this should be an API option
|
|
193
|
-
// Forces the json-schema-ref-parser
|
|
194
|
-
const parseJsonRefs = true;
|
|
195
187
|
if (!(0, index_1.isURL)(openapiPath)) {
|
|
196
188
|
const stat = await fs_extra_1.default.lstat(openapiPath);
|
|
197
189
|
if (stat.isDirectory()) {
|
|
@@ -206,7 +198,7 @@ async function readOpenapiFiles(openapiPath, options) {
|
|
|
206
198
|
return Promise.all(sources.map(async (source) => {
|
|
207
199
|
// TODO: make a function for this
|
|
208
200
|
const fullPath = path_1.default.join(openapiPath, source);
|
|
209
|
-
const data = (await (0,
|
|
201
|
+
const data = (await (0, loadAndResolveSpec_1.loadAndResolveSpec)(fullPath));
|
|
210
202
|
return {
|
|
211
203
|
source: fullPath,
|
|
212
204
|
sourceDirName: path_1.default.dirname(source),
|
|
@@ -215,7 +207,7 @@ async function readOpenapiFiles(openapiPath, options) {
|
|
|
215
207
|
}));
|
|
216
208
|
}
|
|
217
209
|
}
|
|
218
|
-
const data = (await (0,
|
|
210
|
+
const data = (await (0, loadAndResolveSpec_1.loadAndResolveSpec)(openapiPath));
|
|
219
211
|
return [
|
|
220
212
|
{
|
|
221
213
|
source: openapiPath,
|
|
@@ -227,27 +219,39 @@ async function readOpenapiFiles(openapiPath, options) {
|
|
|
227
219
|
exports.readOpenapiFiles = readOpenapiFiles;
|
|
228
220
|
async function processOpenapiFiles(files, sidebarOptions) {
|
|
229
221
|
const promises = files.map(async (file) => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
222
|
+
if (file.data !== undefined) {
|
|
223
|
+
const processedFile = await processOpenapiFile(file.data, sidebarOptions);
|
|
224
|
+
const itemsObjectsArray = processedFile[0].map((item) => ({
|
|
225
|
+
...item,
|
|
226
|
+
}));
|
|
227
|
+
const tags = processedFile[1];
|
|
228
|
+
return [itemsObjectsArray, tags];
|
|
229
|
+
}
|
|
230
|
+
console.warn(chalk_1.default.yellow(`WARNING: the following OpenAPI spec returned undefined: ${file.source}`));
|
|
231
|
+
return [];
|
|
236
232
|
});
|
|
237
233
|
const metadata = await Promise.all(promises);
|
|
238
234
|
const items = metadata
|
|
239
235
|
.map(function (x) {
|
|
240
236
|
return x[0];
|
|
241
237
|
})
|
|
242
|
-
.flat()
|
|
243
|
-
|
|
238
|
+
.flat()
|
|
239
|
+
.filter(function (x) {
|
|
240
|
+
// Remove undefined items due to transient parsing errors
|
|
241
|
+
return x !== undefined;
|
|
242
|
+
});
|
|
243
|
+
const tags = metadata
|
|
244
|
+
.map(function (x) {
|
|
244
245
|
return x[1];
|
|
246
|
+
})
|
|
247
|
+
.filter(function (x) {
|
|
248
|
+
// Remove undefined tags due to transient parsing errors
|
|
249
|
+
return x !== undefined;
|
|
245
250
|
});
|
|
246
251
|
return [items, tags];
|
|
247
252
|
}
|
|
248
253
|
exports.processOpenapiFiles = processOpenapiFiles;
|
|
249
|
-
async function processOpenapiFile(
|
|
250
|
-
const openapiData = await resolveRefs(openapiDataWithRefs);
|
|
254
|
+
async function processOpenapiFile(openapiData, sidebarOptions) {
|
|
251
255
|
const postmanCollection = await createPostmanCollection(openapiData);
|
|
252
256
|
const items = createItems(openapiData, sidebarOptions);
|
|
253
257
|
bindCollectionToApiItems(items, postmanCollection);
|
package/lib/openapi/types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface OpenApiObject {
|
|
|
11
11
|
security?: SecurityRequirementObject[];
|
|
12
12
|
tags?: TagObject[];
|
|
13
13
|
externalDocs?: ExternalDocumentationObject;
|
|
14
|
+
swagger?: string;
|
|
14
15
|
}
|
|
15
16
|
export interface OpenApiObjectWithRef {
|
|
16
17
|
openapi: string;
|
|
@@ -255,7 +256,7 @@ export declare type SchemaObject = Omit<JSONSchema, "type" | "allOf" | "oneOf" |
|
|
|
255
256
|
not?: SchemaObject;
|
|
256
257
|
items?: SchemaObject;
|
|
257
258
|
properties?: Map<SchemaObject>;
|
|
258
|
-
additionalProperties?:
|
|
259
|
+
additionalProperties?: Map<SchemaObject>;
|
|
259
260
|
nullable?: boolean;
|
|
260
261
|
discriminator?: DiscriminatorObject;
|
|
261
262
|
readOnly?: boolean;
|
|
@@ -9,25 +9,62 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
13
|
-
// @ts-nocheck
|
|
12
|
+
exports.loadAndResolveSpec = exports.convertSwagger2OpenAPI = void 0;
|
|
14
13
|
const json_schema_ref_parser_1 = __importDefault(require("@apidevtools/json-schema-ref-parser"));
|
|
15
|
-
const
|
|
16
|
-
const config_1 = require("@redocly/openapi-core/lib/config/config");
|
|
14
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
17
15
|
const chalk_1 = __importDefault(require("chalk"));
|
|
16
|
+
// @ts-ignore
|
|
18
17
|
const swagger2openapi_1 = require("swagger2openapi");
|
|
18
|
+
function serializer(replacer, cycleReplacer) {
|
|
19
|
+
var stack = [], keys = [];
|
|
20
|
+
if (cycleReplacer === undefined)
|
|
21
|
+
cycleReplacer = function (key, value) {
|
|
22
|
+
if (stack[0] === value)
|
|
23
|
+
return "circular()";
|
|
24
|
+
return value.title ? `circular(${value.title})` : "circular()";
|
|
25
|
+
};
|
|
26
|
+
return function (key, value) {
|
|
27
|
+
if (stack.length > 0) {
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
var thisPos = stack.indexOf(this);
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
|
|
32
|
+
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
if (~stack.indexOf(value))
|
|
35
|
+
value = cycleReplacer.call(this, key, value);
|
|
36
|
+
}
|
|
37
|
+
else
|
|
38
|
+
stack.push(value);
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
return replacer === undefined ? value : replacer.call(this, key, value);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function convertSwagger2OpenAPI(spec) {
|
|
44
|
+
console.warn("[ReDoc Compatibility mode]: Converting OpenAPI 2.0 to OpenAPI 3.0");
|
|
45
|
+
return new Promise((resolve, reject) => (0, swagger2openapi_1.convertObj)(spec, { patch: true, warnOnly: true, text: "{}", anchors: true }, (err, res) => {
|
|
46
|
+
// TODO: log any warnings
|
|
47
|
+
if (err) {
|
|
48
|
+
return reject(err);
|
|
49
|
+
}
|
|
50
|
+
resolve(res && res.openapi);
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
exports.convertSwagger2OpenAPI = convertSwagger2OpenAPI;
|
|
19
54
|
async function resolveJsonRefs(specUrlOrObject) {
|
|
20
55
|
var _a, _b;
|
|
21
56
|
try {
|
|
22
57
|
let schema = await json_schema_ref_parser_1.default.dereference(specUrlOrObject, {
|
|
23
58
|
continueOnError: true,
|
|
24
59
|
resolve: {
|
|
60
|
+
file: true,
|
|
61
|
+
external: true,
|
|
25
62
|
http: {
|
|
26
63
|
timeout: 15000, // 15 sec timeout
|
|
27
64
|
},
|
|
28
65
|
},
|
|
29
66
|
dereference: {
|
|
30
|
-
circular:
|
|
67
|
+
circular: true,
|
|
31
68
|
},
|
|
32
69
|
});
|
|
33
70
|
return schema;
|
|
@@ -37,8 +74,8 @@ async function resolveJsonRefs(specUrlOrObject) {
|
|
|
37
74
|
return;
|
|
38
75
|
}
|
|
39
76
|
}
|
|
40
|
-
async function
|
|
41
|
-
const config = new
|
|
77
|
+
async function loadAndResolveSpec(specUrlOrObject) {
|
|
78
|
+
const config = new openapi_core_1.Config({});
|
|
42
79
|
const bundleOpts = {
|
|
43
80
|
config,
|
|
44
81
|
base: process.cwd(),
|
|
@@ -54,26 +91,22 @@ async function loadAndBundleSpec(specUrlOrObject, parseJsonRefs) {
|
|
|
54
91
|
}
|
|
55
92
|
// Force dereference ?
|
|
56
93
|
// bundleOpts["dereference"] = true;
|
|
57
|
-
const { bundle: { parsed }, } = await (0,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
94
|
+
const { bundle: { parsed }, } = await (0, openapi_core_1.bundle)(bundleOpts);
|
|
95
|
+
const resolved = await resolveJsonRefs(parsed);
|
|
96
|
+
// Force serialization and replace circular $ref pointers
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
const serialized = JSON.stringify(resolved, serializer());
|
|
99
|
+
let decycled;
|
|
100
|
+
try {
|
|
101
|
+
decycled = JSON.parse(serialized);
|
|
65
102
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return reject(err);
|
|
75
|
-
}
|
|
76
|
-
resolve(res && res.openapi);
|
|
77
|
-
}));
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.error(chalk_1.default.yellow(err));
|
|
105
|
+
}
|
|
106
|
+
return decycled !== undefined && typeof decycled === "object"
|
|
107
|
+
? decycled.swagger !== undefined
|
|
108
|
+
? convertSwagger2OpenAPI(decycled)
|
|
109
|
+
: decycled
|
|
110
|
+
: resolved;
|
|
78
111
|
}
|
|
79
|
-
exports.
|
|
112
|
+
exports.loadAndResolveSpec = loadAndResolveSpec;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from "../types";
|
|
2
|
+
export declare type MergedOpenAPISchema = OpenAPISchema & {
|
|
3
|
+
parentRefs?: string[];
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Loads and keeps spec. Provides raw spec operations
|
|
7
|
+
*/
|
|
8
|
+
export declare class OpenAPIParser {
|
|
9
|
+
private options;
|
|
10
|
+
specUrl?: string;
|
|
11
|
+
spec: OpenAPISpec;
|
|
12
|
+
private _refCounter;
|
|
13
|
+
private allowMergeRefs;
|
|
14
|
+
constructor(spec: OpenAPISpec, specUrl?: string, options?: {});
|
|
15
|
+
validate(spec: any): void;
|
|
16
|
+
/**
|
|
17
|
+
* get spec part by JsonPointer ($ref)
|
|
18
|
+
*/
|
|
19
|
+
byRef: <T extends unknown = any>(ref: string) => T | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* checks if the object is OpenAPI reference (contains $ref property)
|
|
22
|
+
*/
|
|
23
|
+
isRef(obj: any): obj is OpenAPIRef;
|
|
24
|
+
/**
|
|
25
|
+
* resets visited endpoints. should be run after
|
|
26
|
+
*/
|
|
27
|
+
resetVisited(): void;
|
|
28
|
+
exitRef<T>(ref: Referenced<T>): void;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve given reference object or return as is if it is not a reference
|
|
31
|
+
* @param obj object to dereference
|
|
32
|
+
* @param forceCircular whether to dereference even if it is circular ref
|
|
33
|
+
*/
|
|
34
|
+
deref<T extends object>(obj: OpenAPIRef | T, forceCircular?: boolean, mergeAsAllOf?: boolean): T;
|
|
35
|
+
shallowDeref<T extends unknown>(obj: OpenAPIRef | T): T;
|
|
36
|
+
mergeRefs(ref: any, resolved: any, mergeAsAllOf: boolean): any;
|
|
37
|
+
/**
|
|
38
|
+
* Merge allOf constraints.
|
|
39
|
+
* @param schema schema with allOF
|
|
40
|
+
* @param $ref pointer of the schema
|
|
41
|
+
* @param forceCircular whether to dereference children even if it is a circular ref
|
|
42
|
+
*/
|
|
43
|
+
mergeAllOf(schema: OpenAPISchema, $ref?: string, forceCircular?: boolean, used$Refs?: Set<string>): MergedOpenAPISchema;
|
|
44
|
+
/**
|
|
45
|
+
* Find all derived definitions among #/components/schemas from any of $refs
|
|
46
|
+
* returns map of definition pointer to definition name
|
|
47
|
+
* @param $refs array of references to find derived from
|
|
48
|
+
*/
|
|
49
|
+
findDerived($refs: string[]): Record<string, string[] | string>;
|
|
50
|
+
exitParents(shema: MergedOpenAPISchema): void;
|
|
51
|
+
private hoistOneOfs;
|
|
52
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* ============================================================================
|
|
3
|
+
* Copyright (c) Palo Alto Networks
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
* ========================================================================== */
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.OpenAPIParser = void 0;
|
|
10
|
+
const helpers_1 = require("../utils/helpers");
|
|
11
|
+
const JsonPointer_1 = require("../utils/JsonPointer");
|
|
12
|
+
const openapi_1 = require("../utils/openapi");
|
|
13
|
+
const RedocNormalizedOptions_1 = require("./RedocNormalizedOptions");
|
|
14
|
+
/**
|
|
15
|
+
* Helper class to keep track of visited references to avoid
|
|
16
|
+
* endless recursion because of circular refs
|
|
17
|
+
*/
|
|
18
|
+
class RefCounter {
|
|
19
|
+
constructor() {
|
|
20
|
+
this._counter = {};
|
|
21
|
+
}
|
|
22
|
+
reset() {
|
|
23
|
+
this._counter = {};
|
|
24
|
+
}
|
|
25
|
+
visit(ref) {
|
|
26
|
+
this._counter[ref] = this._counter[ref] ? this._counter[ref] + 1 : 1;
|
|
27
|
+
}
|
|
28
|
+
exit(ref) {
|
|
29
|
+
this._counter[ref] = this._counter[ref] && this._counter[ref] - 1;
|
|
30
|
+
}
|
|
31
|
+
visited(ref) {
|
|
32
|
+
return !!this._counter[ref];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Loads and keeps spec. Provides raw spec operations
|
|
37
|
+
*/
|
|
38
|
+
class OpenAPIParser {
|
|
39
|
+
constructor(spec, specUrl, options = new RedocNormalizedOptions_1.RedocNormalizedOptions()) {
|
|
40
|
+
this.options = options;
|
|
41
|
+
this._refCounter = new RefCounter();
|
|
42
|
+
this.allowMergeRefs = false;
|
|
43
|
+
/**
|
|
44
|
+
* get spec part by JsonPointer ($ref)
|
|
45
|
+
*/
|
|
46
|
+
this.byRef = (ref) => {
|
|
47
|
+
let res;
|
|
48
|
+
if (!this.spec) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (ref.charAt(0) !== "#") {
|
|
52
|
+
ref = "#" + ref;
|
|
53
|
+
}
|
|
54
|
+
ref = decodeURIComponent(ref);
|
|
55
|
+
try {
|
|
56
|
+
res = JsonPointer_1.JsonPointer.get(this.spec, ref);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
// do nothing
|
|
60
|
+
}
|
|
61
|
+
return res || {};
|
|
62
|
+
};
|
|
63
|
+
this.validate(spec);
|
|
64
|
+
this.spec = spec;
|
|
65
|
+
this.allowMergeRefs = spec.openapi.startsWith("3.1");
|
|
66
|
+
const href = undefined;
|
|
67
|
+
if (typeof specUrl === "string") {
|
|
68
|
+
this.specUrl = new URL(specUrl, href).href;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
validate(spec) {
|
|
72
|
+
if (spec.openapi === undefined) {
|
|
73
|
+
throw new Error("Document must be valid OpenAPI 3.0.0 definition");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* checks if the object is OpenAPI reference (contains $ref property)
|
|
78
|
+
*/
|
|
79
|
+
isRef(obj) {
|
|
80
|
+
if (!obj) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return obj.$ref !== undefined && obj.$ref !== null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* resets visited endpoints. should be run after
|
|
87
|
+
*/
|
|
88
|
+
resetVisited() {
|
|
89
|
+
if (process.env.NODE_ENV !== "production") {
|
|
90
|
+
// check in dev mode
|
|
91
|
+
for (const k in this._refCounter._counter) {
|
|
92
|
+
if (this._refCounter._counter[k] > 0) {
|
|
93
|
+
console.warn("Not exited reference: " + k);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this._refCounter = new RefCounter();
|
|
98
|
+
}
|
|
99
|
+
exitRef(ref) {
|
|
100
|
+
if (!this.isRef(ref)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this._refCounter.exit(ref.$ref);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve given reference object or return as is if it is not a reference
|
|
107
|
+
* @param obj object to dereference
|
|
108
|
+
* @param forceCircular whether to dereference even if it is circular ref
|
|
109
|
+
*/
|
|
110
|
+
deref(obj, forceCircular = false, mergeAsAllOf = false) {
|
|
111
|
+
if (this.isRef(obj)) {
|
|
112
|
+
const schemaName = (0, openapi_1.getDefinitionName)(obj.$ref);
|
|
113
|
+
if (schemaName && this.options.ignoreNamedSchemas.has(schemaName)) {
|
|
114
|
+
return { type: "object", title: schemaName };
|
|
115
|
+
}
|
|
116
|
+
const resolved = this.byRef(obj.$ref);
|
|
117
|
+
const visited = this._refCounter.visited(obj.$ref);
|
|
118
|
+
this._refCounter.visit(obj.$ref);
|
|
119
|
+
if (visited && !forceCircular) {
|
|
120
|
+
// circular reference detected
|
|
121
|
+
// tslint:disable-next-line
|
|
122
|
+
return Object.assign({}, resolved, { "x-circular-ref": true });
|
|
123
|
+
}
|
|
124
|
+
// deref again in case one more $ref is here
|
|
125
|
+
let result = resolved;
|
|
126
|
+
if (this.isRef(resolved)) {
|
|
127
|
+
result = this.deref(resolved, false, mergeAsAllOf);
|
|
128
|
+
this.exitRef(resolved);
|
|
129
|
+
}
|
|
130
|
+
return this.allowMergeRefs
|
|
131
|
+
? this.mergeRefs(obj, resolved, mergeAsAllOf)
|
|
132
|
+
: result;
|
|
133
|
+
}
|
|
134
|
+
return obj;
|
|
135
|
+
}
|
|
136
|
+
shallowDeref(obj) {
|
|
137
|
+
if (this.isRef(obj)) {
|
|
138
|
+
const schemaName = (0, openapi_1.getDefinitionName)(obj.$ref);
|
|
139
|
+
if (schemaName && this.options.ignoreNamedSchemas.has(schemaName)) {
|
|
140
|
+
return { type: "object", title: schemaName };
|
|
141
|
+
}
|
|
142
|
+
const resolved = this.byRef(obj.$ref);
|
|
143
|
+
return this.allowMergeRefs
|
|
144
|
+
? this.mergeRefs(obj, resolved, false)
|
|
145
|
+
: resolved;
|
|
146
|
+
}
|
|
147
|
+
return obj;
|
|
148
|
+
}
|
|
149
|
+
mergeRefs(ref, resolved, mergeAsAllOf) {
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
151
|
+
const { $ref, ...rest } = ref;
|
|
152
|
+
const keys = Object.keys(rest);
|
|
153
|
+
if (keys.length === 0) {
|
|
154
|
+
if (this.isRef(resolved)) {
|
|
155
|
+
return this.shallowDeref(resolved);
|
|
156
|
+
}
|
|
157
|
+
return resolved;
|
|
158
|
+
}
|
|
159
|
+
if (mergeAsAllOf &&
|
|
160
|
+
keys.some((k) => k !== "description" && k !== "title" && k !== "externalDocs")) {
|
|
161
|
+
return {
|
|
162
|
+
allOf: [rest, resolved],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// small optimization
|
|
167
|
+
return {
|
|
168
|
+
...resolved,
|
|
169
|
+
...rest,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Merge allOf constraints.
|
|
175
|
+
* @param schema schema with allOF
|
|
176
|
+
* @param $ref pointer of the schema
|
|
177
|
+
* @param forceCircular whether to dereference children even if it is a circular ref
|
|
178
|
+
*/
|
|
179
|
+
mergeAllOf(schema, $ref, forceCircular = false, used$Refs = new Set()) {
|
|
180
|
+
if ($ref) {
|
|
181
|
+
used$Refs.add($ref);
|
|
182
|
+
}
|
|
183
|
+
schema = this.hoistOneOfs(schema);
|
|
184
|
+
if (schema.allOf === undefined) {
|
|
185
|
+
return schema;
|
|
186
|
+
}
|
|
187
|
+
let receiver = {
|
|
188
|
+
...schema,
|
|
189
|
+
allOf: undefined,
|
|
190
|
+
parentRefs: [],
|
|
191
|
+
title: schema.title || (0, openapi_1.getDefinitionName)($ref),
|
|
192
|
+
};
|
|
193
|
+
// avoid mutating inner objects
|
|
194
|
+
if (receiver.properties !== undefined &&
|
|
195
|
+
typeof receiver.properties === "object") {
|
|
196
|
+
receiver.properties = { ...receiver.properties };
|
|
197
|
+
}
|
|
198
|
+
if (receiver.items !== undefined && typeof receiver.items === "object") {
|
|
199
|
+
receiver.items = { ...receiver.items };
|
|
200
|
+
}
|
|
201
|
+
const allOfSchemas = schema.allOf
|
|
202
|
+
.map((subSchema) => {
|
|
203
|
+
if (subSchema && subSchema.$ref && used$Refs.has(subSchema.$ref)) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const resolved = this.deref(subSchema, forceCircular, true);
|
|
207
|
+
const subRef = subSchema.$ref || undefined;
|
|
208
|
+
const subMerged = this.mergeAllOf(resolved, subRef, forceCircular, used$Refs);
|
|
209
|
+
receiver.parentRefs.push(...(subMerged.parentRefs || []));
|
|
210
|
+
return {
|
|
211
|
+
$ref: subRef,
|
|
212
|
+
schema: subMerged,
|
|
213
|
+
};
|
|
214
|
+
})
|
|
215
|
+
.filter((child) => child !== undefined);
|
|
216
|
+
for (const { $ref: subSchemaRef, schema: subSchema } of allOfSchemas) {
|
|
217
|
+
const { type, enum: enumProperty, properties, items, required, oneOf, anyOf, title, ...otherConstraints } = subSchema;
|
|
218
|
+
if (receiver.type !== type &&
|
|
219
|
+
receiver.type !== undefined &&
|
|
220
|
+
type !== undefined) {
|
|
221
|
+
console.warn(`Incompatible types in allOf at "${$ref}": "${receiver.type}" and "${type}"`);
|
|
222
|
+
}
|
|
223
|
+
if (type !== undefined) {
|
|
224
|
+
if (Array.isArray(type) && Array.isArray(receiver.type)) {
|
|
225
|
+
receiver.type = [...type, ...receiver.type];
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
receiver.type = type;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (enumProperty !== undefined) {
|
|
232
|
+
if (Array.isArray(enumProperty) && Array.isArray(receiver.enum)) {
|
|
233
|
+
receiver.enum = [...enumProperty, ...receiver.enum];
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
receiver.enum = enumProperty;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (properties !== undefined) {
|
|
240
|
+
receiver.properties = receiver.properties || {};
|
|
241
|
+
for (const prop in properties) {
|
|
242
|
+
if (!receiver.properties[prop]) {
|
|
243
|
+
receiver.properties[prop] = properties[prop];
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// merge inner properties
|
|
247
|
+
const mergedProp = this.mergeAllOf({ allOf: [receiver.properties[prop], properties[prop]] }, $ref + "/properties/" + prop);
|
|
248
|
+
receiver.properties[prop] = mergedProp;
|
|
249
|
+
this.exitParents(mergedProp); // every prop resolution should have separate recursive stack
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (items !== undefined) {
|
|
254
|
+
const receiverItems = (0, helpers_1.isBoolean)(receiver.items)
|
|
255
|
+
? { items: receiver.items }
|
|
256
|
+
: receiver.items
|
|
257
|
+
? Object.assign({}, receiver.items)
|
|
258
|
+
: {};
|
|
259
|
+
const subSchemaItems = (0, helpers_1.isBoolean)(items)
|
|
260
|
+
? { items }
|
|
261
|
+
: Object.assign({}, items);
|
|
262
|
+
// merge inner properties
|
|
263
|
+
receiver.items = this.mergeAllOf({ allOf: [receiverItems, subSchemaItems] }, $ref + "/items");
|
|
264
|
+
}
|
|
265
|
+
if (required !== undefined) {
|
|
266
|
+
receiver.required = (receiver.required || []).concat(required);
|
|
267
|
+
}
|
|
268
|
+
if (oneOf !== undefined) {
|
|
269
|
+
receiver.oneOf = oneOf;
|
|
270
|
+
}
|
|
271
|
+
if (anyOf !== undefined) {
|
|
272
|
+
receiver.anyOf = anyOf;
|
|
273
|
+
}
|
|
274
|
+
// merge rest of constraints
|
|
275
|
+
// TODO: do more intelligent merge
|
|
276
|
+
receiver = {
|
|
277
|
+
...receiver,
|
|
278
|
+
title: receiver.title || title,
|
|
279
|
+
...otherConstraints,
|
|
280
|
+
};
|
|
281
|
+
if (subSchemaRef) {
|
|
282
|
+
receiver.parentRefs.push(subSchemaRef);
|
|
283
|
+
if (receiver.title === undefined && (0, openapi_1.isNamedDefinition)(subSchemaRef)) {
|
|
284
|
+
// this is not so correct behaviour. commented out for now
|
|
285
|
+
// ref: https://github.com/Redocly/redoc/issues/601
|
|
286
|
+
// receiver.title = JsonPointer.baseName(subSchemaRef);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return receiver;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Find all derived definitions among #/components/schemas from any of $refs
|
|
294
|
+
* returns map of definition pointer to definition name
|
|
295
|
+
* @param $refs array of references to find derived from
|
|
296
|
+
*/
|
|
297
|
+
findDerived($refs) {
|
|
298
|
+
const res = {};
|
|
299
|
+
const schemas = (this.spec.components && this.spec.components.schemas) || {};
|
|
300
|
+
for (const defName in schemas) {
|
|
301
|
+
const def = this.deref(schemas[defName]);
|
|
302
|
+
if (def.allOf !== undefined &&
|
|
303
|
+
def.allOf.find((obj) => obj.$ref !== undefined && $refs.indexOf(obj.$ref) > -1)) {
|
|
304
|
+
res["#/components/schemas/" + defName] = [
|
|
305
|
+
def["x-discriminator-value"] || defName,
|
|
306
|
+
];
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return res;
|
|
310
|
+
}
|
|
311
|
+
exitParents(shema) {
|
|
312
|
+
for (const parent$ref of shema.parentRefs || []) {
|
|
313
|
+
this.exitRef({ $ref: parent$ref });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
hoistOneOfs(schema) {
|
|
317
|
+
if (schema.allOf === undefined) {
|
|
318
|
+
return schema;
|
|
319
|
+
}
|
|
320
|
+
const allOf = schema.allOf;
|
|
321
|
+
for (let i = 0; i < allOf.length; i++) {
|
|
322
|
+
const sub = allOf[i];
|
|
323
|
+
if ((0, helpers_1.isArray)(sub.oneOf)) {
|
|
324
|
+
const beforeAllOf = allOf.slice(0, i);
|
|
325
|
+
const afterAllOf = allOf.slice(i + 1);
|
|
326
|
+
return {
|
|
327
|
+
oneOf: sub.oneOf.map((part) => {
|
|
328
|
+
const merged = this.mergeAllOf({
|
|
329
|
+
allOf: [...beforeAllOf, part, ...afterAllOf],
|
|
330
|
+
});
|
|
331
|
+
// each oneOf should be independent so exiting all the parent refs
|
|
332
|
+
// otherwise it will cause false-positive recursive detection
|
|
333
|
+
this.exitParents(merged);
|
|
334
|
+
return merged;
|
|
335
|
+
}),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return schema;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.OpenAPIParser = OpenAPIParser;
|