docusaurus-plugin-openapi-docs 1.0.2 → 1.0.5

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 (42) hide show
  1. package/README.md +106 -17
  2. package/lib/index.d.ts +7 -1
  3. package/lib/index.js +228 -18
  4. package/lib/markdown/createAuthentication.d.ts +2 -0
  5. package/lib/markdown/createAuthentication.js +139 -0
  6. package/lib/markdown/createParamsDetails.js +2 -0
  7. package/lib/markdown/createSchemaDetails.js +2 -0
  8. package/lib/markdown/index.d.ts +3 -2
  9. package/lib/markdown/index.js +10 -2
  10. package/lib/openapi/createExample.js +59 -49
  11. package/lib/openapi/openapi.d.ts +3 -3
  12. package/lib/openapi/openapi.js +71 -41
  13. package/lib/openapi/openapi.test.js +0 -6
  14. package/lib/openapi/utils/loadAndBundleSpec.d.ts +3 -0
  15. package/lib/openapi/utils/loadAndBundleSpec.js +44 -0
  16. package/lib/openapi/utils/types.d.ts +306 -0
  17. package/lib/openapi/utils/types.js +8 -0
  18. package/lib/options.d.ts +0 -2
  19. package/lib/options.js +36 -7
  20. package/lib/sidebars/index.d.ts +1 -1
  21. package/lib/sidebars/index.js +33 -30
  22. package/lib/sidebars/utils.d.ts +2 -0
  23. package/lib/sidebars/utils.js +31 -0
  24. package/lib/types.d.ts +34 -11
  25. package/package.json +10 -8
  26. package/src/index.ts +291 -20
  27. package/src/markdown/createAuthentication.ts +160 -0
  28. package/src/markdown/createParamsDetails.ts +2 -0
  29. package/src/markdown/createSchemaDetails.ts +2 -0
  30. package/src/markdown/index.ts +15 -2
  31. package/src/openapi/createExample.ts +59 -50
  32. package/src/openapi/openapi.test.ts +0 -6
  33. package/src/openapi/openapi.ts +85 -39
  34. package/src/openapi/utils/loadAndBundleSpec.ts +62 -0
  35. package/src/openapi/utils/types.ts +303 -0
  36. package/src/options.ts +41 -8
  37. package/src/sidebars/index.ts +40 -30
  38. package/src/sidebars/utils.ts +29 -0
  39. package/src/types.ts +35 -9
  40. package/src/openapi/__fixtures__/examples/yogurtstore/_category_.json +0 -4
  41. package/src/openapi/__fixtures__/examples/yogurtstore/froyo.yaml +0 -13
  42. package/src/openapi/__fixtures__/examples/yogurtstore/nested/nested.yaml +0 -13
@@ -5,6 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
+ import chalk from "chalk";
9
+
8
10
  import { SchemaObject } from "./types";
9
11
 
10
12
  interface OASTypeToTypeMap {
@@ -48,71 +50,78 @@ const primitives: Primitives = {
48
50
  };
49
51
 
50
52
  export const sampleFromSchema = (schema: SchemaObject = {}): any => {
51
- let { type, example, allOf, properties, items } = schema;
53
+ try {
54
+ let { type, example, allOf, properties, items } = schema;
52
55
 
53
- if (example !== undefined) {
54
- return example;
55
- }
56
+ if (example !== undefined) {
57
+ return example;
58
+ }
56
59
 
57
- if (allOf) {
58
- // TODO: We are just assuming it will always be an object for now
59
- let obj: SchemaObject = {
60
- type: "object",
61
- properties: {},
62
- required: [], // NOTE: We shouldn't need to worry about required
63
- };
64
- for (let item of allOf) {
65
- if (item.properties) {
66
- obj.properties = {
67
- ...obj.properties,
68
- ...item.properties,
69
- };
60
+ if (allOf) {
61
+ // TODO: We are just assuming it will always be an object for now
62
+ let obj: SchemaObject = {
63
+ type: "object",
64
+ properties: {},
65
+ required: [], // NOTE: We shouldn't need to worry about required
66
+ };
67
+ for (let item of allOf) {
68
+ if (item.properties) {
69
+ obj.properties = {
70
+ ...obj.properties,
71
+ ...item.properties,
72
+ };
73
+ }
70
74
  }
75
+ return sampleFromSchema(obj);
71
76
  }
72
- return sampleFromSchema(obj);
73
- }
74
77
 
75
- if (!type) {
76
- if (properties) {
77
- type = "object";
78
- } else if (items) {
79
- type = "array";
80
- } else {
81
- return;
78
+ if (!type) {
79
+ if (properties) {
80
+ type = "object";
81
+ } else if (items) {
82
+ type = "array";
83
+ } else {
84
+ return;
85
+ }
82
86
  }
83
- }
84
87
 
85
- if (type === "object") {
86
- let obj: any = {};
87
- for (let [name, prop] of Object.entries(properties ?? {})) {
88
- if (prop.deprecated) {
89
- continue;
88
+ if (type === "object") {
89
+ let obj: any = {};
90
+ for (let [name, prop] of Object.entries(properties ?? {})) {
91
+ if (prop.deprecated) {
92
+ continue;
93
+ }
94
+ obj[name] = sampleFromSchema(prop);
90
95
  }
91
- obj[name] = sampleFromSchema(prop);
96
+ return obj;
92
97
  }
93
- return obj;
94
- }
95
98
 
96
- if (type === "array") {
97
- if (Array.isArray(items?.anyOf)) {
98
- return items?.anyOf.map((item) => sampleFromSchema(item));
99
- }
99
+ if (type === "array") {
100
+ if (Array.isArray(items?.anyOf)) {
101
+ return items?.anyOf.map((item) => sampleFromSchema(item));
102
+ }
100
103
 
101
- if (Array.isArray(items?.oneOf)) {
102
- return items?.oneOf.map((item) => sampleFromSchema(item));
103
- }
104
+ if (Array.isArray(items?.oneOf)) {
105
+ return items?.oneOf.map((item) => sampleFromSchema(item));
106
+ }
104
107
 
105
- return [sampleFromSchema(items)];
106
- }
108
+ return [sampleFromSchema(items)];
109
+ }
107
110
 
108
- if (schema.enum) {
109
- if (schema.default) {
110
- return schema.default;
111
+ if (schema.enum) {
112
+ if (schema.default) {
113
+ return schema.default;
114
+ }
115
+ return normalizeArray(schema.enum)[0];
111
116
  }
112
- return normalizeArray(schema.enum)[0];
113
- }
114
117
 
115
- return primitive(schema);
118
+ return primitive(schema);
119
+ } catch (err) {
120
+ console.error(
121
+ chalk.yellow("WARNING: failed to create example from schema object:", err)
122
+ );
123
+ return;
124
+ }
116
125
  };
117
126
 
118
127
  function primitive(schema: SchemaObject = {}) {
@@ -26,12 +26,6 @@ describe("openapi", () => {
26
26
  const yaml = results.find((x) => x.source.endsWith("openapi.yaml"));
27
27
  expect(yaml).toBeTruthy();
28
28
  expect(yaml?.sourceDirName).toBe(".");
29
- const froyo = results.find((x) => x.source.endsWith("froyo.yaml"));
30
- expect(froyo).toBeTruthy();
31
- expect(froyo?.sourceDirName).toBe("yogurtstore");
32
- const nested = results.find((x) => x.source.endsWith("nested.yaml"));
33
- expect(nested).toBeTruthy();
34
- expect(nested?.sourceDirName).toBe("yogurtstore/nested");
35
29
  });
36
30
  });
37
31
  });
@@ -13,13 +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 yaml from "js-yaml";
17
16
  import JsonRefs from "json-refs";
18
17
  import { kebabCase } from "lodash";
19
18
 
20
- import { ApiMetadata, ApiPageMetadata, InfoPageMetadata } from "../types";
19
+ import { isURL } from "../index";
20
+ import {
21
+ ApiMetadata,
22
+ ApiPageMetadata,
23
+ InfoPageMetadata,
24
+ SidebarOptions,
25
+ TagPageMetadata,
26
+ } from "../types";
21
27
  import { sampleFromSchema } from "./createExample";
22
28
  import { OpenApiObject, OpenApiObjectWithRef, TagObject } from "./types";
29
+ import { loadAndBundleSpec } from "./utils/loadAndBundleSpec";
23
30
 
24
31
  /**
25
32
  * Finds any reference objects in the OpenAPI definition and resolves them to a finalized value.
@@ -75,13 +82,45 @@ async function createPostmanCollection(
75
82
 
76
83
  type PartialPage<T> = Omit<T, "permalink" | "source" | "sourceDirName">;
77
84
 
78
- function createItems(openapiData: OpenApiObject): ApiMetadata[] {
85
+ function createItems(
86
+ openapiData: OpenApiObject,
87
+ sidebarOptions: SidebarOptions
88
+ ): ApiMetadata[] {
79
89
  // TODO: Find a better way to handle this
80
90
  let items: PartialPage<ApiMetadata>[] = [];
91
+ const infoId = kebabCase(openapiData.info.title);
92
+
93
+ if (sidebarOptions?.categoryLinkSource === "tag") {
94
+ // Only create an tag pages if categoryLinkSource set to tag.
95
+ const tags: TagObject[] = openapiData.tags ?? [];
96
+ // eslint-disable-next-line array-callback-return
97
+ tags
98
+ .filter((tag) => !tag.description?.includes("SchemaDefinition"))
99
+ // eslint-disable-next-line array-callback-return
100
+ .map((tag) => {
101
+ const description = getTagDisplayName(
102
+ tag.name!,
103
+ openapiData.tags ?? []
104
+ );
105
+ const tagId = kebabCase(tag.name);
106
+ const tagPage: PartialPage<TagPageMetadata> = {
107
+ type: "tag",
108
+ id: tagId,
109
+ unversionedId: tagId,
110
+ title: description ?? "",
111
+ description: description ?? "",
112
+ slug: "/" + tagId,
113
+ frontMatter: {},
114
+ tag: {
115
+ ...tag,
116
+ },
117
+ };
118
+ items.push(tagPage);
119
+ });
120
+ }
81
121
 
82
- // Only create an info page if we have a description.
83
122
  if (openapiData.info.description) {
84
- const infoId = kebabCase(openapiData.info.title);
123
+ // Only create an info page if we have a description.
85
124
  const infoPage: PartialPage<InfoPageMetadata> = {
86
125
  type: "info",
87
126
  id: infoId,
@@ -90,6 +129,7 @@ function createItems(openapiData: OpenApiObject): ApiMetadata[] {
90
129
  description: openapiData.info.description,
91
130
  slug: "/" + infoId,
92
131
  frontMatter: {},
132
+ securitySchemes: openapiData.components?.securitySchemes,
93
133
  info: {
94
134
  ...openapiData.info,
95
135
  tags: openapiData.tags?.map((tagName) =>
@@ -145,6 +185,7 @@ function createItems(openapiData: OpenApiObject): ApiMetadata[] {
145
185
  const apiPage: PartialPage<ApiPageMetadata> = {
146
186
  type: "api",
147
187
  id: baseId,
188
+ infoId: infoId ?? "",
148
189
  unversionedId: baseId,
149
190
  title: title,
150
191
  description: description ?? "",
@@ -186,7 +227,7 @@ function bindCollectionToApiItems(
186
227
  .replace(/:([a-z0-9-_]+)/gi, "{$1}"); // replace "/:variableName" with "/{variableName}"
187
228
 
188
229
  const apiItem = items.find((item) => {
189
- if (item.type === "info") {
230
+ if (item.type === "info" || item.type === "tag") {
190
231
  return false;
191
232
  }
192
233
  return item.api.path === path && item.api.method === method;
@@ -208,36 +249,39 @@ export async function readOpenapiFiles(
208
249
  openapiPath: string,
209
250
  _options: {}
210
251
  ): Promise<OpenApiFiles[]> {
211
- const stat = await fs.lstat(openapiPath);
212
- if (stat.isDirectory()) {
213
- console.warn(
214
- chalk.yellow(
215
- "WARNING: Loading a directory of OpenAPI definitions is experimental and subject to unannounced breaking changes."
216
- )
217
- );
218
-
219
- // TODO: Add config for inlcude/ignore
220
- const allFiles = await Globby(["**/*.{json,yaml,yml}"], {
221
- cwd: openapiPath,
222
- ignore: GlobExcludeDefault,
223
- });
224
- const sources = allFiles.filter((x) => !x.includes("_category_")); // todo: regex exclude?
225
- return Promise.all(
226
- sources.map(async (source) => {
227
- // TODO: make a function for this
228
- const fullPath = path.join(openapiPath, source);
229
- const openapiString = await fs.readFile(fullPath, "utf-8");
230
- const data = yaml.load(openapiString) as OpenApiObjectWithRef;
231
- return {
232
- source: fullPath, // This will be aliased in process.
233
- sourceDirName: path.dirname(source),
234
- data,
235
- };
236
- })
237
- );
252
+ if (!isURL(openapiPath)) {
253
+ const stat = await fs.lstat(openapiPath);
254
+ if (stat.isDirectory()) {
255
+ console.warn(
256
+ chalk.yellow(
257
+ "WARNING: Loading a directory of OpenAPI definitions is experimental and subject to unannounced breaking changes."
258
+ )
259
+ );
260
+
261
+ // TODO: Add config for inlcude/ignore
262
+ const allFiles = await Globby(["**/*.{json,yaml,yml}"], {
263
+ cwd: openapiPath,
264
+ ignore: GlobExcludeDefault,
265
+ deep: 1,
266
+ });
267
+ const sources = allFiles.filter((x) => !x.includes("_category_")); // todo: regex exclude?
268
+ return Promise.all(
269
+ sources.map(async (source) => {
270
+ // TODO: make a function for this
271
+ const fullPath = path.join(openapiPath, source);
272
+ const data = (await loadAndBundleSpec(
273
+ fullPath
274
+ )) as OpenApiObjectWithRef;
275
+ return {
276
+ source: fullPath, // This will be aliased in process.
277
+ sourceDirName: path.dirname(source),
278
+ data,
279
+ };
280
+ })
281
+ );
282
+ }
238
283
  }
239
- const openapiString = await fs.readFile(openapiPath, "utf-8");
240
- const data = yaml.load(openapiString) as OpenApiObjectWithRef;
284
+ const data = (await loadAndBundleSpec(openapiPath)) as OpenApiObjectWithRef;
241
285
  return [
242
286
  {
243
287
  source: openapiPath, // This will be aliased in process.
@@ -248,10 +292,11 @@ export async function readOpenapiFiles(
248
292
  }
249
293
 
250
294
  export async function processOpenapiFiles(
251
- files: OpenApiFiles[]
295
+ files: OpenApiFiles[],
296
+ sidebarOptions: SidebarOptions
252
297
  ): Promise<[ApiMetadata[], TagObject[]]> {
253
298
  const promises = files.map(async (file) => {
254
- const processedFile = await processOpenapiFile(file.data);
299
+ const processedFile = await processOpenapiFile(file.data, sidebarOptions);
255
300
  const itemsObjectsArray = processedFile[0].map((item) => ({
256
301
  ...item,
257
302
  }));
@@ -271,11 +316,12 @@ export async function processOpenapiFiles(
271
316
  }
272
317
 
273
318
  export async function processOpenapiFile(
274
- openapiDataWithRefs: OpenApiObjectWithRef
319
+ openapiDataWithRefs: OpenApiObjectWithRef,
320
+ sidebarOptions: SidebarOptions
275
321
  ): Promise<[ApiMetadata[], TagObject[]]> {
276
322
  const openapiData = await resolveRefs(openapiDataWithRefs);
277
323
  const postmanCollection = await createPostmanCollection(openapiData);
278
- const items = createItems(openapiData);
324
+ const items = createItems(openapiData, sidebarOptions);
279
325
 
280
326
  bindCollectionToApiItems(items, postmanCollection);
281
327
 
@@ -0,0 +1,62 @@
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
+ // @ts-nocheck
9
+
10
+ import type { Source, Document } from "@redocly/openapi-core";
11
+ import { bundle } from "@redocly/openapi-core/lib/bundle";
12
+ import type { ResolvedConfig } from "@redocly/openapi-core/lib/config";
13
+ import { Config } from "@redocly/openapi-core/lib/config/config";
14
+ import { convertObj } from "swagger2openapi";
15
+
16
+ import { OpenAPISpec } from "./types";
17
+
18
+ export async function loadAndBundleSpec(
19
+ specUrlOrObject: object | string
20
+ ): Promise<OpenAPISpec> {
21
+ const config = new Config({} as ResolvedConfig);
22
+ const bundleOpts = {
23
+ config,
24
+ base: process.cwd(),
25
+ };
26
+
27
+ if (typeof specUrlOrObject === "object" && specUrlOrObject !== null) {
28
+ bundleOpts["doc"] = {
29
+ source: { absoluteRef: "" } as Source,
30
+ parsed: specUrlOrObject,
31
+ } as Document;
32
+ } else {
33
+ bundleOpts["ref"] = specUrlOrObject;
34
+ }
35
+
36
+ // Force dereference ?
37
+ // bundleOpts["dereference"] = true;
38
+
39
+ const {
40
+ bundle: { parsed },
41
+ } = await bundle(bundleOpts);
42
+ return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
43
+ }
44
+
45
+ export function convertSwagger2OpenAPI(spec: any): Promise<OpenAPISpec> {
46
+ console.warn(
47
+ "[ReDoc Compatibility mode]: Converting OpenAPI 2.0 to OpenAPI 3.0"
48
+ );
49
+ return new Promise<OpenAPISpec>((resolve, reject) =>
50
+ convertObj(
51
+ spec,
52
+ { patch: true, warnOnly: true, text: "{}", anchors: true },
53
+ (err, res) => {
54
+ // TODO: log any warnings
55
+ if (err) {
56
+ return reject(err);
57
+ }
58
+ resolve(res && (res.openapi as any));
59
+ }
60
+ )
61
+ );
62
+ }