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.
Files changed (56) hide show
  1. package/README.md +5 -4
  2. package/lib/index.js +3 -3
  3. package/lib/markdown/createSchemaDetails.js +325 -132
  4. package/lib/markdown/index.js +1 -0
  5. package/lib/markdown/schema.js +25 -9
  6. package/lib/markdown/utils.d.ts +1 -1
  7. package/lib/markdown/utils.js +4 -1
  8. package/lib/openapi/openapi.d.ts +5 -5
  9. package/lib/openapi/openapi.js +27 -23
  10. package/lib/openapi/openapi.test.js +1 -1
  11. package/lib/openapi/types.d.ts +2 -1
  12. package/lib/openapi/utils/loadAndResolveSpec.d.ts +2 -0
  13. package/lib/openapi/utils/loadAndResolveSpec.js +112 -0
  14. package/lib/openapi/utils/services/OpenAPIParser.d.ts +52 -0
  15. package/lib/openapi/utils/services/OpenAPIParser.js +342 -0
  16. package/lib/openapi/utils/services/RedocNormalizedOptions.d.ts +100 -0
  17. package/lib/openapi/utils/services/RedocNormalizedOptions.js +170 -0
  18. package/lib/openapi/utils/types/index.d.ts +2 -0
  19. package/lib/openapi/utils/types/index.js +23 -0
  20. package/lib/openapi/utils/types/open-api.d.ts +305 -0
  21. package/lib/openapi/utils/types/open-api.js +8 -0
  22. package/lib/openapi/utils/utils/JsonPointer.d.ts +51 -0
  23. package/lib/openapi/utils/utils/JsonPointer.js +95 -0
  24. package/lib/openapi/utils/utils/helpers.d.ts +43 -0
  25. package/lib/openapi/utils/utils/helpers.js +230 -0
  26. package/lib/openapi/utils/utils/index.d.ts +3 -0
  27. package/lib/openapi/utils/utils/index.js +25 -0
  28. package/lib/openapi/utils/utils/openapi.d.ts +40 -0
  29. package/lib/openapi/utils/utils/openapi.js +605 -0
  30. package/lib/options.js +1 -1
  31. package/lib/sidebars/index.js +9 -5
  32. package/lib/types.d.ts +1 -1
  33. package/package.json +16 -11
  34. package/src/index.ts +3 -3
  35. package/src/markdown/createSchemaDetails.ts +405 -159
  36. package/src/markdown/index.ts +1 -0
  37. package/src/markdown/schema.ts +28 -8
  38. package/src/markdown/utils.ts +5 -2
  39. package/src/openapi/openapi.test.ts +1 -1
  40. package/src/openapi/openapi.ts +39 -29
  41. package/src/openapi/types.ts +2 -1
  42. package/src/openapi/utils/loadAndResolveSpec.ts +123 -0
  43. package/src/openapi/utils/services/OpenAPIParser.ts +433 -0
  44. package/src/openapi/utils/services/RedocNormalizedOptions.ts +330 -0
  45. package/src/openapi/utils/types/index.ts +10 -0
  46. package/src/openapi/utils/types/open-api.ts +303 -0
  47. package/src/openapi/utils/utils/JsonPointer.ts +99 -0
  48. package/src/openapi/utils/utils/helpers.ts +239 -0
  49. package/src/openapi/utils/utils/index.ts +11 -0
  50. package/src/openapi/utils/utils/openapi.ts +771 -0
  51. package/src/options.ts +1 -1
  52. package/src/sidebars/index.ts +11 -6
  53. package/src/types.ts +1 -1
  54. package/lib/openapi/utils/loadAndBundleSpec.d.ts +0 -3
  55. package/lib/openapi/utils/loadAndBundleSpec.js +0 -44
  56. package/src/openapi/utils/loadAndBundleSpec.ts +0 -62
@@ -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
  }
@@ -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
- return children.filter((c) => c !== undefined).join("");
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")
@@ -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, OpenApiObjectWithRef, TagObject } from "./types";
29
- import { loadAndBundleSpec } from "./utils/loadAndBundleSpec";
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 = JSON.parse(JSON.stringify(openapiData)) as OpenApiObject;
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: OpenApiObjectWithRef;
237
+ data: OpenApiObject;
246
238
  }
247
239
 
248
240
  export async function readOpenapiFiles(
249
241
  openapiPath: string,
250
- _options: {}
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 loadAndBundleSpec(
264
+ const data = (await loadAndResolveSpec(
273
265
  fullPath
274
- )) as OpenApiObjectWithRef;
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 loadAndBundleSpec(openapiPath)) as OpenApiObjectWithRef;
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
- const processedFile = await processOpenapiFile(file.data, sidebarOptions);
300
- const itemsObjectsArray = processedFile[0].map((item) => ({
301
- ...item,
302
- }));
303
- const tags = processedFile[1];
304
- return [itemsObjectsArray, tags];
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
- const tags = metadata.map(function (x) {
313
- return x[1];
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
- openapiDataWithRefs: OpenApiObjectWithRef,
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
 
@@ -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?: boolean | SchemaObject;
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
+ }