docusaurus-plugin-openapi-docs 1.0.4 → 1.1.0
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 +5 -4
- package/lib/index.js +3 -3
- 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 +5 -5
- package/lib/openapi/openapi.js +27 -23
- package/lib/openapi/openapi.test.js +1 -1
- package/lib/openapi/types.d.ts +2 -1
- package/lib/openapi/utils/loadAndResolveSpec.d.ts +2 -0
- package/lib/openapi/utils/loadAndResolveSpec.js +112 -0
- 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/options.js +1 -1
- package/lib/sidebars/index.js +9 -5
- package/lib/types.d.ts +1 -1
- package/package.json +16 -11
- package/src/index.ts +3 -3
- 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.test.ts +1 -1
- package/src/openapi/openapi.ts +39 -29
- 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/options.ts +1 -1
- package/src/sidebars/index.ts +11 -6
- package/src/types.ts +1 -1
- package/lib/openapi/utils/loadAndBundleSpec.d.ts +0 -3
- package/lib/openapi/utils/loadAndBundleSpec.js +0 -44
- package/src/openapi/utils/loadAndBundleSpec.ts +0 -62
package/src/markdown/schema.ts
CHANGED
|
@@ -8,22 +8,36 @@
|
|
|
8
8
|
import { SchemaObject } from "../openapi/types";
|
|
9
9
|
|
|
10
10
|
function prettyName(schema: SchemaObject, circular?: boolean) {
|
|
11
|
-
if (schema.$ref) {
|
|
12
|
-
return schema.$ref.replace("#/components/schemas/", "") + circular
|
|
13
|
-
? " (circular)"
|
|
14
|
-
: "";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
if (schema.format) {
|
|
18
12
|
return schema.format;
|
|
19
13
|
}
|
|
20
14
|
|
|
21
15
|
if (schema.allOf) {
|
|
16
|
+
if (typeof schema.allOf[0] === "string") {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
if (schema.allOf[0].includes("circular")) {
|
|
19
|
+
return schema.allOf[0];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return "object";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (schema.oneOf) {
|
|
26
|
+
return "object";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (schema.anyOf) {
|
|
22
30
|
return "object";
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
if (schema.type === "object") {
|
|
26
34
|
return schema.xml?.name ?? schema.type;
|
|
35
|
+
// return schema.type;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (schema.type === "array") {
|
|
39
|
+
return schema.xml?.name ?? schema.type;
|
|
40
|
+
// return schema.type;
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
return schema.title ?? schema.type;
|
|
@@ -42,8 +56,6 @@ export function getSchemaName(
|
|
|
42
56
|
|
|
43
57
|
export function getQualifierMessage(schema?: SchemaObject): string | undefined {
|
|
44
58
|
// TODO:
|
|
45
|
-
// - maxItems
|
|
46
|
-
// - minItems
|
|
47
59
|
// - uniqueItems
|
|
48
60
|
// - maxProperties
|
|
49
61
|
// - minProperties
|
|
@@ -107,6 +119,14 @@ export function getQualifierMessage(schema?: SchemaObject): string | undefined {
|
|
|
107
119
|
qualifierGroups.push(`[${schema.enum.map((e) => `\`${e}\``).join(", ")}]`);
|
|
108
120
|
}
|
|
109
121
|
|
|
122
|
+
if (schema.minItems) {
|
|
123
|
+
qualifierGroups.push(`items >= ${schema.minItems}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (schema.maxItems) {
|
|
127
|
+
qualifierGroups.push(`items <= ${schema.maxItems}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
if (qualifierGroups.length === 0) {
|
|
111
131
|
return undefined;
|
|
112
132
|
}
|
package/src/markdown/utils.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
* ========================================================================== */
|
|
7
7
|
|
|
8
|
-
export type Children = string | undefined | (string | undefined)[];
|
|
8
|
+
export type Children = string | undefined | (string | string[] | undefined)[];
|
|
9
9
|
|
|
10
10
|
export type Props = Record<string, any> & { children?: Children };
|
|
11
11
|
|
|
@@ -33,7 +33,10 @@ export function guard<T>(
|
|
|
33
33
|
|
|
34
34
|
export function render(children: Children): string {
|
|
35
35
|
if (Array.isArray(children)) {
|
|
36
|
-
|
|
36
|
+
const filteredChildren = children.filter((c) => c !== undefined);
|
|
37
|
+
return filteredChildren
|
|
38
|
+
.map((i: any) => (Array.isArray(i) ? i.join("") : i))
|
|
39
|
+
.join("");
|
|
37
40
|
}
|
|
38
41
|
return children ?? "";
|
|
39
42
|
}
|
|
@@ -16,7 +16,7 @@ describe("openapi", () => {
|
|
|
16
16
|
it("readOpenapiFiles", async () => {
|
|
17
17
|
const results = await readOpenapiFiles(
|
|
18
18
|
path.join(__dirname, "__fixtures__/examples"),
|
|
19
|
-
{}
|
|
19
|
+
{ specPath: "./", outputDir: "./" }
|
|
20
20
|
);
|
|
21
21
|
const categoryMeta = results.find((x) =>
|
|
22
22
|
x.source.endsWith("_category_.json")
|
package/src/openapi/openapi.ts
CHANGED
|
@@ -13,28 +13,20 @@ import sdk from "@paloaltonetworks/postman-collection";
|
|
|
13
13
|
import Collection from "@paloaltonetworks/postman-collection";
|
|
14
14
|
import chalk from "chalk";
|
|
15
15
|
import fs from "fs-extra";
|
|
16
|
-
import JsonRefs from "json-refs";
|
|
17
16
|
import { kebabCase } from "lodash";
|
|
18
17
|
|
|
19
18
|
import { isURL } from "../index";
|
|
20
19
|
import {
|
|
21
20
|
ApiMetadata,
|
|
21
|
+
APIOptions,
|
|
22
22
|
ApiPageMetadata,
|
|
23
23
|
InfoPageMetadata,
|
|
24
24
|
SidebarOptions,
|
|
25
25
|
TagPageMetadata,
|
|
26
26
|
} from "../types";
|
|
27
27
|
import { sampleFromSchema } from "./createExample";
|
|
28
|
-
import { OpenApiObject,
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Finds any reference objects in the OpenAPI definition and resolves them to a finalized value.
|
|
33
|
-
*/
|
|
34
|
-
async function resolveRefs(openapiData: OpenApiObjectWithRef) {
|
|
35
|
-
const { resolved } = await JsonRefs.resolveRefs(openapiData);
|
|
36
|
-
return resolved as OpenApiObject;
|
|
37
|
-
}
|
|
28
|
+
import { OpenApiObject, TagObject } from "./types";
|
|
29
|
+
import { loadAndResolveSpec } from "./utils/loadAndResolveSpec";
|
|
38
30
|
|
|
39
31
|
/**
|
|
40
32
|
* Convenience function for converting raw JSON to a Postman Collection object.
|
|
@@ -61,7 +53,7 @@ function jsonToCollection(data: OpenApiObject): Promise<Collection> {
|
|
|
61
53
|
async function createPostmanCollection(
|
|
62
54
|
openapiData: OpenApiObject
|
|
63
55
|
): Promise<Collection> {
|
|
64
|
-
const data =
|
|
56
|
+
const data = openapiData as OpenApiObject;
|
|
65
57
|
|
|
66
58
|
// Including `servers` breaks postman, so delete all of them.
|
|
67
59
|
delete data.servers;
|
|
@@ -242,12 +234,12 @@ function bindCollectionToApiItems(
|
|
|
242
234
|
interface OpenApiFiles {
|
|
243
235
|
source: string;
|
|
244
236
|
sourceDirName: string;
|
|
245
|
-
data:
|
|
237
|
+
data: OpenApiObject;
|
|
246
238
|
}
|
|
247
239
|
|
|
248
240
|
export async function readOpenapiFiles(
|
|
249
241
|
openapiPath: string,
|
|
250
|
-
|
|
242
|
+
options: APIOptions
|
|
251
243
|
): Promise<OpenApiFiles[]> {
|
|
252
244
|
if (!isURL(openapiPath)) {
|
|
253
245
|
const stat = await fs.lstat(openapiPath);
|
|
@@ -269,9 +261,9 @@ export async function readOpenapiFiles(
|
|
|
269
261
|
sources.map(async (source) => {
|
|
270
262
|
// TODO: make a function for this
|
|
271
263
|
const fullPath = path.join(openapiPath, source);
|
|
272
|
-
const data = (await
|
|
264
|
+
const data = (await loadAndResolveSpec(
|
|
273
265
|
fullPath
|
|
274
|
-
)) as
|
|
266
|
+
)) as unknown as OpenApiObject;
|
|
275
267
|
return {
|
|
276
268
|
source: fullPath, // This will be aliased in process.
|
|
277
269
|
sourceDirName: path.dirname(source),
|
|
@@ -281,7 +273,9 @@ export async function readOpenapiFiles(
|
|
|
281
273
|
);
|
|
282
274
|
}
|
|
283
275
|
}
|
|
284
|
-
const data = (await
|
|
276
|
+
const data = (await loadAndResolveSpec(
|
|
277
|
+
openapiPath
|
|
278
|
+
)) as unknown as OpenApiObject;
|
|
285
279
|
return [
|
|
286
280
|
{
|
|
287
281
|
source: openapiPath, // This will be aliased in process.
|
|
@@ -296,30 +290,46 @@ export async function processOpenapiFiles(
|
|
|
296
290
|
sidebarOptions: SidebarOptions
|
|
297
291
|
): Promise<[ApiMetadata[], TagObject[]]> {
|
|
298
292
|
const promises = files.map(async (file) => {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
293
|
+
if (file.data !== undefined) {
|
|
294
|
+
const processedFile = await processOpenapiFile(file.data, sidebarOptions);
|
|
295
|
+
const itemsObjectsArray = processedFile[0].map((item) => ({
|
|
296
|
+
...item,
|
|
297
|
+
}));
|
|
298
|
+
const tags = processedFile[1];
|
|
299
|
+
return [itemsObjectsArray, tags];
|
|
300
|
+
}
|
|
301
|
+
console.warn(
|
|
302
|
+
chalk.yellow(
|
|
303
|
+
`WARNING: the following OpenAPI spec returned undefined: ${file.source}`
|
|
304
|
+
)
|
|
305
|
+
);
|
|
306
|
+
return [];
|
|
305
307
|
});
|
|
306
308
|
const metadata = await Promise.all(promises);
|
|
307
309
|
const items = metadata
|
|
308
310
|
.map(function (x) {
|
|
309
311
|
return x[0];
|
|
310
312
|
})
|
|
311
|
-
.flat()
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
.flat()
|
|
314
|
+
.filter(function (x) {
|
|
315
|
+
// Remove undefined items due to transient parsing errors
|
|
316
|
+
return x !== undefined;
|
|
317
|
+
});
|
|
318
|
+
const tags = metadata
|
|
319
|
+
.map(function (x) {
|
|
320
|
+
return x[1];
|
|
321
|
+
})
|
|
322
|
+
.filter(function (x) {
|
|
323
|
+
// Remove undefined tags due to transient parsing errors
|
|
324
|
+
return x !== undefined;
|
|
325
|
+
});
|
|
315
326
|
return [items as ApiMetadata[], tags as TagObject[]];
|
|
316
327
|
}
|
|
317
328
|
|
|
318
329
|
export async function processOpenapiFile(
|
|
319
|
-
|
|
330
|
+
openapiData: OpenApiObject,
|
|
320
331
|
sidebarOptions: SidebarOptions
|
|
321
332
|
): Promise<[ApiMetadata[], TagObject[]]> {
|
|
322
|
-
const openapiData = await resolveRefs(openapiDataWithRefs);
|
|
323
333
|
const postmanCollection = await createPostmanCollection(openapiData);
|
|
324
334
|
const items = createItems(openapiData, sidebarOptions);
|
|
325
335
|
|
package/src/openapi/types.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface OpenApiObject {
|
|
|
20
20
|
security?: SecurityRequirementObject[];
|
|
21
21
|
tags?: TagObject[];
|
|
22
22
|
externalDocs?: ExternalDocumentationObject;
|
|
23
|
+
swagger?: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface OpenApiObjectWithRef {
|
|
@@ -325,7 +326,7 @@ export type SchemaObject = Omit<
|
|
|
325
326
|
not?: SchemaObject;
|
|
326
327
|
items?: SchemaObject;
|
|
327
328
|
properties?: Map<SchemaObject>;
|
|
328
|
-
additionalProperties?:
|
|
329
|
+
additionalProperties?: Map<SchemaObject>;
|
|
329
330
|
|
|
330
331
|
// OpenAPI additions
|
|
331
332
|
nullable?: boolean;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
9
|
+
import { bundle, Config } from "@redocly/openapi-core";
|
|
10
|
+
import type { Source, Document } from "@redocly/openapi-core";
|
|
11
|
+
import { ResolvedConfig } from "@redocly/openapi-core/lib/config";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
import { convertObj } from "swagger2openapi";
|
|
15
|
+
|
|
16
|
+
import { OpenApiObject } from "../types";
|
|
17
|
+
|
|
18
|
+
function serializer(replacer: any, cycleReplacer: any) {
|
|
19
|
+
var stack: any = [],
|
|
20
|
+
keys: any = [];
|
|
21
|
+
|
|
22
|
+
if (cycleReplacer === undefined)
|
|
23
|
+
cycleReplacer = function (key: any, value: any) {
|
|
24
|
+
if (stack[0] === value) return "circular()";
|
|
25
|
+
return value.title ? `circular(${value.title})` : "circular()";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return function (key: any, value: any) {
|
|
29
|
+
if (stack.length > 0) {
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
var thisPos = stack.indexOf(this);
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
|
|
34
|
+
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value);
|
|
37
|
+
} else stack.push(value);
|
|
38
|
+
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
return replacer === undefined ? value : replacer.call(this, key, value);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function convertSwagger2OpenAPI(spec: object) {
|
|
45
|
+
console.warn(
|
|
46
|
+
"[ReDoc Compatibility mode]: Converting OpenAPI 2.0 to OpenAPI 3.0"
|
|
47
|
+
);
|
|
48
|
+
return new Promise((resolve, reject) =>
|
|
49
|
+
convertObj(
|
|
50
|
+
spec,
|
|
51
|
+
{ patch: true, warnOnly: true, text: "{}", anchors: true },
|
|
52
|
+
(err: any, res: any) => {
|
|
53
|
+
// TODO: log any warnings
|
|
54
|
+
if (err) {
|
|
55
|
+
return reject(err);
|
|
56
|
+
}
|
|
57
|
+
resolve(res && res.openapi);
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function resolveJsonRefs(specUrlOrObject: object | string) {
|
|
64
|
+
try {
|
|
65
|
+
let schema = await $RefParser.dereference(specUrlOrObject, {
|
|
66
|
+
continueOnError: true,
|
|
67
|
+
resolve: {
|
|
68
|
+
file: true,
|
|
69
|
+
external: true,
|
|
70
|
+
http: {
|
|
71
|
+
timeout: 15000, // 15 sec timeout
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
dereference: {
|
|
75
|
+
circular: true,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return schema as OpenApiObject;
|
|
79
|
+
} catch (err: any) {
|
|
80
|
+
console.error(chalk.yellow(err.errors[0]?.message ?? err));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function loadAndResolveSpec(specUrlOrObject: object | string) {
|
|
86
|
+
const config = new Config({} as ResolvedConfig);
|
|
87
|
+
const bundleOpts = {
|
|
88
|
+
config,
|
|
89
|
+
base: process.cwd(),
|
|
90
|
+
} as any;
|
|
91
|
+
|
|
92
|
+
if (typeof specUrlOrObject === "object" && specUrlOrObject !== null) {
|
|
93
|
+
bundleOpts["doc"] = {
|
|
94
|
+
source: { absoluteRef: "" } as Source,
|
|
95
|
+
parsed: specUrlOrObject,
|
|
96
|
+
} as Document;
|
|
97
|
+
} else {
|
|
98
|
+
bundleOpts["ref"] = specUrlOrObject;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Force dereference ?
|
|
102
|
+
// bundleOpts["dereference"] = true;
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
bundle: { parsed },
|
|
106
|
+
} = await bundle(bundleOpts);
|
|
107
|
+
const resolved = await resolveJsonRefs(parsed);
|
|
108
|
+
|
|
109
|
+
// Force serialization and replace circular $ref pointers
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
const serialized = JSON.stringify(resolved, serializer());
|
|
112
|
+
let decycled;
|
|
113
|
+
try {
|
|
114
|
+
decycled = JSON.parse(serialized);
|
|
115
|
+
} catch (err: any) {
|
|
116
|
+
console.error(chalk.yellow(err));
|
|
117
|
+
}
|
|
118
|
+
return decycled !== undefined && typeof decycled === "object"
|
|
119
|
+
? decycled.swagger !== undefined
|
|
120
|
+
? convertSwagger2OpenAPI(decycled)
|
|
121
|
+
: decycled
|
|
122
|
+
: resolved;
|
|
123
|
+
}
|