docusaurus-plugin-openapi-docs 0.0.0-1000
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/LICENSE +21 -0
- package/README.md +361 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +709 -0
- package/lib/markdown/createArrayBracket.d.ts +2 -0
- package/lib/markdown/createArrayBracket.js +36 -0
- package/lib/markdown/createAuthentication.d.ts +2 -0
- package/lib/markdown/createAuthentication.js +171 -0
- package/lib/markdown/createAuthorization.d.ts +1 -0
- package/lib/markdown/createAuthorization.js +15 -0
- package/lib/markdown/createCallbackMethodEndpoint.d.ts +1 -0
- package/lib/markdown/createCallbackMethodEndpoint.js +20 -0
- package/lib/markdown/createCallbacks.d.ts +6 -0
- package/lib/markdown/createCallbacks.js +77 -0
- package/lib/markdown/createContactInfo.d.ts +2 -0
- package/lib/markdown/createContactInfo.js +39 -0
- package/lib/markdown/createDeprecationNotice.d.ts +6 -0
- package/lib/markdown/createDeprecationNotice.js +20 -0
- package/lib/markdown/createDescription.d.ts +1 -0
- package/lib/markdown/createDescription.js +13 -0
- package/lib/markdown/createDetails.d.ts +2 -0
- package/lib/markdown/createDetails.js +17 -0
- package/lib/markdown/createDetailsSummary.d.ts +2 -0
- package/lib/markdown/createDetailsSummary.js +17 -0
- package/lib/markdown/createDownload.d.ts +1 -0
- package/lib/markdown/createDownload.js +16 -0
- package/lib/markdown/createHeading.d.ts +1 -0
- package/lib/markdown/createHeading.js +20 -0
- package/lib/markdown/createLicense.d.ts +2 -0
- package/lib/markdown/createLicense.js +32 -0
- package/lib/markdown/createLogo.d.ts +2 -0
- package/lib/markdown/createLogo.js +18 -0
- package/lib/markdown/createMethodEndpoint.d.ts +1 -0
- package/lib/markdown/createMethodEndpoint.js +20 -0
- package/lib/markdown/createParamsDetails.d.ts +6 -0
- package/lib/markdown/createParamsDetails.js +18 -0
- package/lib/markdown/createRequestBodyDetails.d.ts +13 -0
- package/lib/markdown/createRequestBodyDetails.js +13 -0
- package/lib/markdown/createRequestHeader.d.ts +1 -0
- package/lib/markdown/createRequestHeader.js +21 -0
- package/lib/markdown/createRequestSchema.d.ts +14 -0
- package/lib/markdown/createRequestSchema.js +20 -0
- package/lib/markdown/createResponseSchema.d.ts +14 -0
- package/lib/markdown/createResponseSchema.js +20 -0
- package/lib/markdown/createSchema.d.ts +9 -0
- package/lib/markdown/createSchema.js +668 -0
- package/lib/markdown/createSchema.test.d.ts +1 -0
- package/lib/markdown/createSchema.test.js +913 -0
- package/lib/markdown/createStatusCodes.d.ts +9 -0
- package/lib/markdown/createStatusCodes.js +63 -0
- package/lib/markdown/createTermsOfService.d.ts +1 -0
- package/lib/markdown/createTermsOfService.js +31 -0
- package/lib/markdown/createVendorExtensions.d.ts +1 -0
- package/lib/markdown/createVendorExtensions.js +24 -0
- package/lib/markdown/createVersionBadge.d.ts +1 -0
- package/lib/markdown/createVersionBadge.js +19 -0
- package/lib/markdown/index.d.ts +5 -0
- package/lib/markdown/index.js +92 -0
- package/lib/markdown/schema.d.ts +3 -0
- package/lib/markdown/schema.js +154 -0
- package/lib/markdown/schema.test.d.ts +1 -0
- package/lib/markdown/schema.test.js +181 -0
- package/lib/markdown/utils.d.ts +20 -0
- package/lib/markdown/utils.js +68 -0
- package/lib/openapi/createRequestExample.d.ts +2 -0
- package/lib/openapi/createRequestExample.js +191 -0
- package/lib/openapi/createResponseExample.d.ts +2 -0
- package/lib/openapi/createResponseExample.js +192 -0
- package/lib/openapi/index.d.ts +1 -0
- package/lib/openapi/index.js +12 -0
- package/lib/openapi/openapi.d.ts +12 -0
- package/lib/openapi/openapi.js +544 -0
- package/lib/openapi/openapi.test.d.ts +1 -0
- package/lib/openapi/openapi.test.js +33 -0
- package/lib/openapi/types.d.ts +354 -0
- package/lib/openapi/types.js +8 -0
- package/lib/openapi/utils/loadAndResolveSpec.d.ts +2 -0
- package/lib/openapi/utils/loadAndResolveSpec.js +153 -0
- package/lib/openapi/utils/services/OpenAPIParser.d.ts +52 -0
- package/lib/openapi/utils/services/OpenAPIParser.js +343 -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/types.d.ts +306 -0
- package/lib/openapi/utils/types.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.d.ts +2 -0
- package/lib/options.js +69 -0
- package/lib/sidebars/index.d.ts +4 -0
- package/lib/sidebars/index.js +226 -0
- package/lib/sidebars/utils.d.ts +2 -0
- package/lib/sidebars/utils.js +30 -0
- package/lib/types.d.ts +135 -0
- package/lib/types.js +8 -0
- package/package.json +69 -0
- package/src/index.ts +945 -0
- package/src/markdown/__snapshots__/createSchema.test.ts.snap +1605 -0
- package/src/markdown/createArrayBracket.ts +35 -0
- package/src/markdown/createAuthentication.ts +201 -0
- package/src/markdown/createAuthorization.ts +13 -0
- package/src/markdown/createCallbackMethodEndpoint.ts +19 -0
- package/src/markdown/createCallbacks.ts +101 -0
- package/src/markdown/createContactInfo.ts +41 -0
- package/src/markdown/createDeprecationNotice.ts +31 -0
- package/src/markdown/createDescription.ts +12 -0
- package/src/markdown/createDetails.ts +16 -0
- package/src/markdown/createDetailsSummary.ts +16 -0
- package/src/markdown/createDownload.ts +15 -0
- package/src/markdown/createHeading.ts +23 -0
- package/src/markdown/createLicense.ts +34 -0
- package/src/markdown/createLogo.ts +21 -0
- package/src/markdown/createMethodEndpoint.ts +19 -0
- package/src/markdown/createParamsDetails.ts +22 -0
- package/src/markdown/createRequestBodyDetails.ts +24 -0
- package/src/markdown/createRequestHeader.ts +24 -0
- package/src/markdown/createRequestSchema.ts +32 -0
- package/src/markdown/createResponseSchema.ts +32 -0
- package/src/markdown/createSchema.test.ts +1075 -0
- package/src/markdown/createSchema.ts +862 -0
- package/src/markdown/createStatusCodes.ts +63 -0
- package/src/markdown/createTermsOfService.ts +30 -0
- package/src/markdown/createVendorExtensions.ts +22 -0
- package/src/markdown/createVersionBadge.ts +22 -0
- package/src/markdown/index.ts +144 -0
- package/src/markdown/schema.test.ts +208 -0
- package/src/markdown/schema.ts +185 -0
- package/src/markdown/utils.ts +89 -0
- package/src/openapi/__fixtures__/examples/openapi.yaml +49 -0
- package/src/openapi/createRequestExample.ts +235 -0
- package/src/openapi/createResponseExample.ts +238 -0
- package/src/openapi/index.ts +8 -0
- package/src/openapi/openapi.test.ts +40 -0
- package/src/openapi/openapi.ts +697 -0
- package/src/openapi/types.ts +455 -0
- package/src/openapi/utils/loadAndResolveSpec.ts +171 -0
- package/src/openapi/utils/services/OpenAPIParser.ts +434 -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/types.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/openapi-to-postmanv2.d.ts +10 -0
- package/src/options.ts +77 -0
- package/src/plugin-content-docs-types.d.ts +42 -0
- package/src/plugin-openapi.d.ts +87 -0
- package/src/postman-collection.d.ts +10 -0
- package/src/sidebars/index.ts +322 -0
- package/src/sidebars/utils.ts +29 -0
- package/src/types.ts +176 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,697 @@
|
|
|
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 path from "path";
|
|
9
|
+
|
|
10
|
+
import { Globby, GlobExcludeDefault, posixPath } from "@docusaurus/utils";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import fs from "fs-extra";
|
|
13
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
14
|
+
import kebabCase from "lodash/kebabCase";
|
|
15
|
+
import unionBy from "lodash/unionBy";
|
|
16
|
+
import uniq from "lodash/uniq";
|
|
17
|
+
import Converter from "openapi-to-postmanv2";
|
|
18
|
+
import Collection from "postman-collection";
|
|
19
|
+
import sdk from "postman-collection";
|
|
20
|
+
|
|
21
|
+
import { sampleRequestFromSchema } from "./createRequestExample";
|
|
22
|
+
import { OpenApiObject, TagGroupObject, TagObject } from "./types";
|
|
23
|
+
import { isURL } from "../index";
|
|
24
|
+
import {
|
|
25
|
+
ApiMetadata,
|
|
26
|
+
APIOptions,
|
|
27
|
+
ApiPageMetadata,
|
|
28
|
+
InfoPageMetadata,
|
|
29
|
+
SchemaPageMetadata,
|
|
30
|
+
SidebarOptions,
|
|
31
|
+
TagPageMetadata,
|
|
32
|
+
} from "../types";
|
|
33
|
+
import { sampleResponseFromSchema } from "./createResponseExample";
|
|
34
|
+
import { loadAndResolveSpec } from "./utils/loadAndResolveSpec";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convenience function for converting raw JSON to a Postman Collection object.
|
|
38
|
+
*/
|
|
39
|
+
function jsonToCollection(data: OpenApiObject): Promise<Collection> {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
let schemaPack = new Converter.SchemaPack(
|
|
42
|
+
{ type: "json", data },
|
|
43
|
+
{ schemaFaker: false }
|
|
44
|
+
);
|
|
45
|
+
schemaPack.computedOptions.schemaFaker = false;
|
|
46
|
+
schemaPack.convert((_err: any, conversionResult: any) => {
|
|
47
|
+
if (!conversionResult.result) {
|
|
48
|
+
return reject(conversionResult.reason);
|
|
49
|
+
}
|
|
50
|
+
return resolve(new sdk.Collection(conversionResult.output[0].data));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a Postman Collection object from an OpenAPI definition.
|
|
57
|
+
*/
|
|
58
|
+
async function createPostmanCollection(
|
|
59
|
+
openapiData: OpenApiObject
|
|
60
|
+
): Promise<Collection> {
|
|
61
|
+
// Create copy of openapiData
|
|
62
|
+
const data = cloneDeep(openapiData) as OpenApiObject;
|
|
63
|
+
|
|
64
|
+
// Including `servers` breaks postman, so delete all of them.
|
|
65
|
+
delete data.servers;
|
|
66
|
+
for (let pathItemObject of Object.values(data.paths)) {
|
|
67
|
+
delete pathItemObject.servers;
|
|
68
|
+
delete pathItemObject.get?.servers;
|
|
69
|
+
delete pathItemObject.put?.servers;
|
|
70
|
+
delete pathItemObject.post?.servers;
|
|
71
|
+
delete pathItemObject.delete?.servers;
|
|
72
|
+
delete pathItemObject.options?.servers;
|
|
73
|
+
delete pathItemObject.head?.servers;
|
|
74
|
+
delete pathItemObject.patch?.servers;
|
|
75
|
+
delete pathItemObject.trace?.servers;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return await jsonToCollection(data);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type PartialPage<T> = Omit<T, "permalink" | "source" | "sourceDirName">;
|
|
82
|
+
|
|
83
|
+
function createItems(
|
|
84
|
+
openapiData: OpenApiObject,
|
|
85
|
+
options: APIOptions,
|
|
86
|
+
sidebarOptions: SidebarOptions
|
|
87
|
+
): ApiMetadata[] {
|
|
88
|
+
// TODO: Find a better way to handle this
|
|
89
|
+
let items: PartialPage<ApiMetadata>[] = [];
|
|
90
|
+
const infoIdSpaces = openapiData.info.title.replace(" ", "-").toLowerCase();
|
|
91
|
+
const infoId = kebabCase(infoIdSpaces);
|
|
92
|
+
|
|
93
|
+
if (openapiData.info.description || openapiData.info.title) {
|
|
94
|
+
// Only create an info page if we have a description.
|
|
95
|
+
const infoDescription = openapiData.info?.description;
|
|
96
|
+
let splitDescription: any;
|
|
97
|
+
if (infoDescription) {
|
|
98
|
+
splitDescription = infoDescription.match(/[^\r\n]+/g);
|
|
99
|
+
}
|
|
100
|
+
const infoPage: PartialPage<InfoPageMetadata> = {
|
|
101
|
+
type: "info",
|
|
102
|
+
id: infoId,
|
|
103
|
+
unversionedId: infoId,
|
|
104
|
+
title: openapiData.info.title
|
|
105
|
+
? openapiData.info.title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
106
|
+
: "",
|
|
107
|
+
description: openapiData.info.description
|
|
108
|
+
? openapiData.info.description.replace(
|
|
109
|
+
/((?:^|[^\\])(?:\\{2})*)"/g,
|
|
110
|
+
"$1'"
|
|
111
|
+
)
|
|
112
|
+
: "",
|
|
113
|
+
frontMatter: {
|
|
114
|
+
description: splitDescription
|
|
115
|
+
? splitDescription[0]
|
|
116
|
+
.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
117
|
+
.replace(/\s+$/, "")
|
|
118
|
+
: "",
|
|
119
|
+
},
|
|
120
|
+
securitySchemes: openapiData.components?.securitySchemes,
|
|
121
|
+
info: {
|
|
122
|
+
...openapiData.info,
|
|
123
|
+
tags: openapiData.tags,
|
|
124
|
+
title: openapiData.info.title ?? "Introduction",
|
|
125
|
+
logo: openapiData.info["x-logo"]! as any,
|
|
126
|
+
darkLogo: openapiData.info["x-dark-logo"]! as any,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
items.push(infoPage);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (let [path, pathObject] of Object.entries(openapiData.paths)) {
|
|
133
|
+
const { $ref, description, parameters, servers, summary, ...rest } =
|
|
134
|
+
pathObject;
|
|
135
|
+
for (let [method, operationObject] of Object.entries({ ...rest })) {
|
|
136
|
+
const title =
|
|
137
|
+
operationObject.summary ??
|
|
138
|
+
operationObject.operationId ??
|
|
139
|
+
"Missing summary";
|
|
140
|
+
if (operationObject.description === undefined) {
|
|
141
|
+
operationObject.description =
|
|
142
|
+
operationObject.summary ?? operationObject.operationId ?? "";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const baseId = operationObject.operationId
|
|
146
|
+
? kebabCase(operationObject.operationId)
|
|
147
|
+
: kebabCase(operationObject.summary);
|
|
148
|
+
|
|
149
|
+
const extensions = [];
|
|
150
|
+
const commonExtensions = ["x-codeSamples"];
|
|
151
|
+
|
|
152
|
+
for (const [key, value] of Object.entries(operationObject)) {
|
|
153
|
+
if (key.startsWith("x-") && !commonExtensions.includes(key)) {
|
|
154
|
+
extensions.push({ key, value });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const servers =
|
|
159
|
+
operationObject.servers ?? pathObject.servers ?? openapiData.servers;
|
|
160
|
+
|
|
161
|
+
const security = operationObject.security ?? openapiData.security;
|
|
162
|
+
|
|
163
|
+
// Add security schemes so we know how to handle security.
|
|
164
|
+
const securitySchemes = openapiData.components?.securitySchemes;
|
|
165
|
+
|
|
166
|
+
// Make sure schemes are lowercase. See: https://github.com/cloud-annotations/docusaurus-plugin-openapi/issues/79
|
|
167
|
+
if (securitySchemes) {
|
|
168
|
+
for (let securityScheme of Object.values(securitySchemes)) {
|
|
169
|
+
if (securityScheme.type === "http") {
|
|
170
|
+
securityScheme.scheme = securityScheme.scheme.toLowerCase();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let jsonRequestBodyExample;
|
|
176
|
+
const content = operationObject.requestBody?.content;
|
|
177
|
+
let body;
|
|
178
|
+
for (let key in content) {
|
|
179
|
+
if (
|
|
180
|
+
key.toLowerCase() === "application/json" ||
|
|
181
|
+
key.toLowerCase() === "application/json; charset=utf-8"
|
|
182
|
+
) {
|
|
183
|
+
body = content[key];
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (body?.schema) {
|
|
189
|
+
jsonRequestBodyExample = sampleRequestFromSchema(body.schema);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Handle vendor JSON media types
|
|
193
|
+
const bodyContent = operationObject.requestBody?.content;
|
|
194
|
+
|
|
195
|
+
if (bodyContent && Object.keys(bodyContent).length > 0) {
|
|
196
|
+
const firstBodyContentKey = Object.keys(bodyContent)[0];
|
|
197
|
+
if (firstBodyContentKey.endsWith("+json")) {
|
|
198
|
+
const firstBody = bodyContent[firstBodyContentKey];
|
|
199
|
+
if (firstBody?.schema) {
|
|
200
|
+
jsonRequestBodyExample = sampleRequestFromSchema(firstBody.schema);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// TODO: Don't include summary temporarilly
|
|
206
|
+
const { summary, ...defaults } = operationObject;
|
|
207
|
+
|
|
208
|
+
// Merge common parameters with operation parameters
|
|
209
|
+
// Operation params take precendence over common params
|
|
210
|
+
if (parameters !== undefined) {
|
|
211
|
+
if (operationObject.parameters !== undefined) {
|
|
212
|
+
defaults.parameters = unionBy(
|
|
213
|
+
operationObject.parameters,
|
|
214
|
+
parameters,
|
|
215
|
+
"name"
|
|
216
|
+
);
|
|
217
|
+
} else {
|
|
218
|
+
defaults.parameters = parameters;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const opDescription = operationObject.description;
|
|
223
|
+
let splitDescription: any;
|
|
224
|
+
if (opDescription) {
|
|
225
|
+
splitDescription = opDescription.match(/[^\r\n]+/g);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const apiPage: PartialPage<ApiPageMetadata> = {
|
|
229
|
+
type: "api",
|
|
230
|
+
id: baseId,
|
|
231
|
+
infoId: infoId ?? "",
|
|
232
|
+
unversionedId: baseId,
|
|
233
|
+
title: title ? title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'") : "",
|
|
234
|
+
description: operationObject.description
|
|
235
|
+
? operationObject.description.replace(
|
|
236
|
+
/((?:^|[^\\])(?:\\{2})*)"/g,
|
|
237
|
+
"$1'"
|
|
238
|
+
)
|
|
239
|
+
: "",
|
|
240
|
+
frontMatter: {
|
|
241
|
+
description: splitDescription
|
|
242
|
+
? splitDescription[0]
|
|
243
|
+
.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
244
|
+
.replace(/\s+$/, "")
|
|
245
|
+
: "",
|
|
246
|
+
...(options?.proxy && { proxy: options.proxy }),
|
|
247
|
+
...(options?.hideSendButton && {
|
|
248
|
+
hide_send_button: options.hideSendButton,
|
|
249
|
+
}),
|
|
250
|
+
...(options?.showExtensions && {
|
|
251
|
+
show_extensions: options.showExtensions,
|
|
252
|
+
}),
|
|
253
|
+
},
|
|
254
|
+
api: {
|
|
255
|
+
...defaults,
|
|
256
|
+
...(extensions.length > 0 && { extensions: extensions }),
|
|
257
|
+
tags: operationObject.tags,
|
|
258
|
+
method,
|
|
259
|
+
path,
|
|
260
|
+
servers,
|
|
261
|
+
security,
|
|
262
|
+
securitySchemes,
|
|
263
|
+
jsonRequestBodyExample,
|
|
264
|
+
info: openapiData.info,
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
items.push(apiPage);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Gather x-webhooks endpoints
|
|
273
|
+
for (let [path, pathObject] of Object.entries(
|
|
274
|
+
openapiData["x-webhooks"] ?? openapiData["webhooks"] ?? {}
|
|
275
|
+
)) {
|
|
276
|
+
path = "webhook";
|
|
277
|
+
const { $ref, description, parameters, servers, summary, ...rest } =
|
|
278
|
+
pathObject;
|
|
279
|
+
for (let [method, operationObject] of Object.entries({ ...rest })) {
|
|
280
|
+
method = "event";
|
|
281
|
+
const title =
|
|
282
|
+
operationObject.summary ??
|
|
283
|
+
operationObject.operationId ??
|
|
284
|
+
"Missing summary";
|
|
285
|
+
if (operationObject.description === undefined) {
|
|
286
|
+
operationObject.description =
|
|
287
|
+
operationObject.summary ?? operationObject.operationId ?? "";
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const baseId = operationObject.operationId
|
|
291
|
+
? kebabCase(operationObject.operationId)
|
|
292
|
+
: kebabCase(operationObject.summary);
|
|
293
|
+
|
|
294
|
+
const extensions = [];
|
|
295
|
+
const commonExtensions = ["x-codeSamples"];
|
|
296
|
+
|
|
297
|
+
for (const [key, value] of Object.entries(operationObject)) {
|
|
298
|
+
if (key.startsWith("x-") && !commonExtensions.includes(key)) {
|
|
299
|
+
extensions.push({ key, value });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const servers =
|
|
304
|
+
operationObject.servers ?? pathObject.servers ?? openapiData.servers;
|
|
305
|
+
|
|
306
|
+
const security = operationObject.security ?? openapiData.security;
|
|
307
|
+
|
|
308
|
+
// Add security schemes so we know how to handle security.
|
|
309
|
+
const securitySchemes = openapiData.components?.securitySchemes;
|
|
310
|
+
|
|
311
|
+
// Make sure schemes are lowercase. See: https://github.com/cloud-annotations/docusaurus-plugin-openapi/issues/79
|
|
312
|
+
if (securitySchemes) {
|
|
313
|
+
for (let securityScheme of Object.values(securitySchemes)) {
|
|
314
|
+
if (securityScheme.type === "http") {
|
|
315
|
+
securityScheme.scheme = securityScheme.scheme.toLowerCase();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let jsonRequestBodyExample;
|
|
321
|
+
const content = operationObject.requestBody?.content;
|
|
322
|
+
let body;
|
|
323
|
+
for (let key in content) {
|
|
324
|
+
if (
|
|
325
|
+
key.toLowerCase() === "application/json" ||
|
|
326
|
+
key.toLowerCase() === "application/json; charset=utf-8"
|
|
327
|
+
) {
|
|
328
|
+
body = content[key];
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (body?.schema) {
|
|
334
|
+
jsonRequestBodyExample = sampleRequestFromSchema(body.schema);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Handle vendor JSON media types
|
|
338
|
+
const bodyContent = operationObject.requestBody?.content;
|
|
339
|
+
if (bodyContent) {
|
|
340
|
+
const firstBodyContentKey = Object.keys(bodyContent)[0];
|
|
341
|
+
if (firstBodyContentKey.endsWith("+json")) {
|
|
342
|
+
const firstBody = bodyContent[firstBodyContentKey];
|
|
343
|
+
if (firstBody?.schema) {
|
|
344
|
+
jsonRequestBodyExample = sampleRequestFromSchema(firstBody.schema);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// TODO: Don't include summary temporarilly
|
|
350
|
+
const { summary, ...defaults } = operationObject;
|
|
351
|
+
|
|
352
|
+
// Merge common parameters with operation parameters
|
|
353
|
+
// Operation params take precendence over common params
|
|
354
|
+
if (parameters !== undefined) {
|
|
355
|
+
if (operationObject.parameters !== undefined) {
|
|
356
|
+
defaults.parameters = unionBy(
|
|
357
|
+
operationObject.parameters,
|
|
358
|
+
parameters,
|
|
359
|
+
"name"
|
|
360
|
+
);
|
|
361
|
+
} else {
|
|
362
|
+
defaults.parameters = parameters;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const opDescription = operationObject.description;
|
|
367
|
+
let splitDescription: any;
|
|
368
|
+
if (opDescription) {
|
|
369
|
+
splitDescription = opDescription.match(/[^\r\n]+/g);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const apiPage: PartialPage<ApiPageMetadata> = {
|
|
373
|
+
type: "api",
|
|
374
|
+
id: baseId,
|
|
375
|
+
infoId: infoId ?? "",
|
|
376
|
+
unversionedId: baseId,
|
|
377
|
+
title: title ? title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'") : "",
|
|
378
|
+
description: operationObject.description
|
|
379
|
+
? operationObject.description.replace(
|
|
380
|
+
/((?:^|[^\\])(?:\\{2})*)"/g,
|
|
381
|
+
"$1'"
|
|
382
|
+
)
|
|
383
|
+
: "",
|
|
384
|
+
frontMatter: {
|
|
385
|
+
description: splitDescription
|
|
386
|
+
? splitDescription[0]
|
|
387
|
+
.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
388
|
+
.replace(/\s+$/, "")
|
|
389
|
+
: "",
|
|
390
|
+
...(options?.proxy && { proxy: options.proxy }),
|
|
391
|
+
...(options?.hideSendButton && {
|
|
392
|
+
hide_send_button: options.hideSendButton,
|
|
393
|
+
}),
|
|
394
|
+
...(options?.showExtensions && {
|
|
395
|
+
show_extensions: options.showExtensions,
|
|
396
|
+
}),
|
|
397
|
+
},
|
|
398
|
+
api: {
|
|
399
|
+
...defaults,
|
|
400
|
+
...(extensions.length > 0 && { extensions: extensions }),
|
|
401
|
+
tags: operationObject.tags,
|
|
402
|
+
method,
|
|
403
|
+
path,
|
|
404
|
+
servers,
|
|
405
|
+
security,
|
|
406
|
+
securitySchemes,
|
|
407
|
+
jsonRequestBodyExample,
|
|
408
|
+
info: openapiData.info,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
items.push(apiPage);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (
|
|
416
|
+
options?.showSchemas === true ||
|
|
417
|
+
Object.entries(openapiData?.components?.schemas ?? {})
|
|
418
|
+
.flatMap(([_, s]) => s["x-tags"])
|
|
419
|
+
.filter((item) => !!item).length > 0
|
|
420
|
+
) {
|
|
421
|
+
// Gather schemas
|
|
422
|
+
for (let [schema, schemaObject] of Object.entries(
|
|
423
|
+
openapiData?.components?.schemas ?? {}
|
|
424
|
+
)) {
|
|
425
|
+
if (options?.showSchemas === true || schemaObject["x-tags"]) {
|
|
426
|
+
const baseIdSpaces =
|
|
427
|
+
schemaObject?.title?.replace(" ", "-").toLowerCase() ?? "";
|
|
428
|
+
const baseId = kebabCase(baseIdSpaces);
|
|
429
|
+
|
|
430
|
+
const schemaDescription = schemaObject.description;
|
|
431
|
+
let splitDescription: any;
|
|
432
|
+
if (schemaDescription) {
|
|
433
|
+
splitDescription = schemaDescription.match(/[^\r\n]+/g);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const schemaPage: PartialPage<SchemaPageMetadata> = {
|
|
437
|
+
type: "schema",
|
|
438
|
+
id: baseId,
|
|
439
|
+
infoId: infoId ?? "",
|
|
440
|
+
unversionedId: baseId,
|
|
441
|
+
title: schemaObject.title
|
|
442
|
+
? schemaObject.title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
443
|
+
: schema,
|
|
444
|
+
description: schemaObject.description
|
|
445
|
+
? schemaObject.description.replace(
|
|
446
|
+
/((?:^|[^\\])(?:\\{2})*)"/g,
|
|
447
|
+
"$1'"
|
|
448
|
+
)
|
|
449
|
+
: "",
|
|
450
|
+
frontMatter: {
|
|
451
|
+
description: splitDescription
|
|
452
|
+
? splitDescription[0]
|
|
453
|
+
.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
454
|
+
.replace(/\s+$/, "")
|
|
455
|
+
: "",
|
|
456
|
+
sample: JSON.stringify(sampleResponseFromSchema(schemaObject)),
|
|
457
|
+
},
|
|
458
|
+
schema: schemaObject,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
items.push(schemaPage);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (sidebarOptions?.categoryLinkSource === "tag") {
|
|
467
|
+
// Get global tags
|
|
468
|
+
const tags: TagObject[] = openapiData.tags ?? [];
|
|
469
|
+
|
|
470
|
+
// Get operation tags
|
|
471
|
+
const apiItems = items.filter((item) => {
|
|
472
|
+
return item.type === "api";
|
|
473
|
+
}) as ApiPageMetadata[];
|
|
474
|
+
const operationTags = uniq(
|
|
475
|
+
apiItems
|
|
476
|
+
.flatMap((item) => item.api.tags)
|
|
477
|
+
.filter((item): item is string => !!item)
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
// eslint-disable-next-line array-callback-return
|
|
481
|
+
tags
|
|
482
|
+
.filter((tag) => operationTags.includes(tag.name!)) // include only tags referenced by operation
|
|
483
|
+
// eslint-disable-next-line array-callback-return
|
|
484
|
+
.map((tag) => {
|
|
485
|
+
const description = getTagDisplayName(
|
|
486
|
+
tag.name!,
|
|
487
|
+
openapiData.tags ?? []
|
|
488
|
+
);
|
|
489
|
+
const tagId = kebabCase(tag.name);
|
|
490
|
+
const splitDescription = description.match(/[^\r\n]+/g);
|
|
491
|
+
const tagPage: PartialPage<TagPageMetadata> = {
|
|
492
|
+
type: "tag",
|
|
493
|
+
id: tagId,
|
|
494
|
+
unversionedId: tagId,
|
|
495
|
+
title: description ?? "",
|
|
496
|
+
description: description ?? "",
|
|
497
|
+
frontMatter: {
|
|
498
|
+
description: splitDescription
|
|
499
|
+
? splitDescription[0]
|
|
500
|
+
.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
|
|
501
|
+
.replace(/\s+$/, "")
|
|
502
|
+
: "",
|
|
503
|
+
},
|
|
504
|
+
tag: {
|
|
505
|
+
...tag,
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
items.push(tagPage);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return items as ApiMetadata[];
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Attach Postman Request objects to the corresponding ApiItems.
|
|
517
|
+
*/
|
|
518
|
+
function bindCollectionToApiItems(
|
|
519
|
+
items: ApiMetadata[],
|
|
520
|
+
postmanCollection: sdk.Collection
|
|
521
|
+
) {
|
|
522
|
+
postmanCollection.forEachItem((item: any) => {
|
|
523
|
+
const method = item.request.method.toLowerCase();
|
|
524
|
+
const path = item.request.url
|
|
525
|
+
.getPath({ unresolved: true }) // unresolved returns "/:variableName" instead of "/<type>"
|
|
526
|
+
.replace(/(?<![a-z0-9-_]+):([a-z0-9-_]+)/gi, "{$1}"); // replace "/:variableName" with "/{variableName}"
|
|
527
|
+
const apiItem = items.find((item) => {
|
|
528
|
+
if (
|
|
529
|
+
item.type === "info" ||
|
|
530
|
+
item.type === "tag" ||
|
|
531
|
+
item.type === "schema"
|
|
532
|
+
) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
return item.api.path === path && item.api.method === method;
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
if (apiItem?.type === "api") {
|
|
539
|
+
apiItem.api.postman = item.request;
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
interface OpenApiFiles {
|
|
545
|
+
source: string;
|
|
546
|
+
sourceDirName: string;
|
|
547
|
+
data: OpenApiObject;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export async function readOpenapiFiles(
|
|
551
|
+
openapiPath: string
|
|
552
|
+
): Promise<OpenApiFiles[]> {
|
|
553
|
+
if (!isURL(openapiPath)) {
|
|
554
|
+
const stat = await fs.lstat(openapiPath);
|
|
555
|
+
if (stat.isDirectory()) {
|
|
556
|
+
// TODO: Add config for inlcude/ignore
|
|
557
|
+
const allFiles = await Globby(["**/*.{json,yaml,yml}"], {
|
|
558
|
+
cwd: openapiPath,
|
|
559
|
+
ignore: GlobExcludeDefault,
|
|
560
|
+
deep: 1,
|
|
561
|
+
});
|
|
562
|
+
const sources = allFiles.filter((x) => !x.includes("_category_")); // todo: regex exclude?
|
|
563
|
+
return Promise.all(
|
|
564
|
+
sources.map(async (source) => {
|
|
565
|
+
// TODO: make a function for this
|
|
566
|
+
const fullPath = posixPath(path.join(openapiPath, source));
|
|
567
|
+
const data = (await loadAndResolveSpec(
|
|
568
|
+
fullPath
|
|
569
|
+
)) as unknown as OpenApiObject;
|
|
570
|
+
return {
|
|
571
|
+
source: fullPath, // This will be aliased in process.
|
|
572
|
+
sourceDirName: path.dirname(source),
|
|
573
|
+
data,
|
|
574
|
+
};
|
|
575
|
+
})
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const data = (await loadAndResolveSpec(
|
|
580
|
+
openapiPath
|
|
581
|
+
)) as unknown as OpenApiObject;
|
|
582
|
+
return [
|
|
583
|
+
{
|
|
584
|
+
source: openapiPath, // This will be aliased in process.
|
|
585
|
+
sourceDirName: ".",
|
|
586
|
+
data,
|
|
587
|
+
},
|
|
588
|
+
];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export async function processOpenapiFiles(
|
|
592
|
+
files: OpenApiFiles[],
|
|
593
|
+
options: APIOptions,
|
|
594
|
+
sidebarOptions: SidebarOptions
|
|
595
|
+
): Promise<[ApiMetadata[], TagObject[][], TagGroupObject[]]> {
|
|
596
|
+
const promises = files.map(async (file) => {
|
|
597
|
+
if (file.data !== undefined) {
|
|
598
|
+
const processedFile = await processOpenapiFile(
|
|
599
|
+
file.data,
|
|
600
|
+
options,
|
|
601
|
+
sidebarOptions
|
|
602
|
+
);
|
|
603
|
+
const itemsObjectsArray = processedFile[0].map((item) => ({
|
|
604
|
+
...item,
|
|
605
|
+
}));
|
|
606
|
+
const tags = processedFile[1];
|
|
607
|
+
const tagGroups = processedFile[2];
|
|
608
|
+
return [itemsObjectsArray, tags, tagGroups];
|
|
609
|
+
}
|
|
610
|
+
console.warn(
|
|
611
|
+
chalk.yellow(
|
|
612
|
+
`WARNING: the following OpenAPI spec returned undefined: ${file.source}`
|
|
613
|
+
)
|
|
614
|
+
);
|
|
615
|
+
return [];
|
|
616
|
+
});
|
|
617
|
+
const metadata = await Promise.all(promises);
|
|
618
|
+
const items = metadata
|
|
619
|
+
.map(function (x) {
|
|
620
|
+
return x[0];
|
|
621
|
+
})
|
|
622
|
+
.flat()
|
|
623
|
+
.filter(function (x) {
|
|
624
|
+
// Remove undefined items due to transient parsing errors
|
|
625
|
+
return x !== undefined;
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const tags = metadata
|
|
629
|
+
.map(function (x) {
|
|
630
|
+
return x[1];
|
|
631
|
+
})
|
|
632
|
+
.filter(function (x) {
|
|
633
|
+
// Remove undefined tags due to transient parsing errors
|
|
634
|
+
return x !== undefined;
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
const tagGroups = metadata
|
|
638
|
+
.map(function (x) {
|
|
639
|
+
return x[2];
|
|
640
|
+
})
|
|
641
|
+
.flat()
|
|
642
|
+
.filter(function (x) {
|
|
643
|
+
// Remove undefined tags due to transient parsing errors
|
|
644
|
+
return x !== undefined;
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
return [
|
|
648
|
+
items as ApiMetadata[],
|
|
649
|
+
tags as TagObject[][],
|
|
650
|
+
tagGroups as TagGroupObject[],
|
|
651
|
+
];
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
export async function processOpenapiFile(
|
|
655
|
+
openapiData: OpenApiObject,
|
|
656
|
+
options: APIOptions,
|
|
657
|
+
sidebarOptions: SidebarOptions
|
|
658
|
+
): Promise<[ApiMetadata[], TagObject[], TagGroupObject[]]> {
|
|
659
|
+
const postmanCollection = await createPostmanCollection(openapiData);
|
|
660
|
+
const items = createItems(openapiData, options, sidebarOptions);
|
|
661
|
+
|
|
662
|
+
bindCollectionToApiItems(items, postmanCollection);
|
|
663
|
+
|
|
664
|
+
let tags: TagObject[] = [];
|
|
665
|
+
if (openapiData.tags !== undefined) {
|
|
666
|
+
tags = openapiData.tags;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
let tagGroups: TagGroupObject[] = [];
|
|
670
|
+
if (openapiData["x-tagGroups"] !== undefined) {
|
|
671
|
+
tagGroups = openapiData["x-tagGroups"];
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
return [items, tags, tagGroups];
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// order for picking items as a display name of tags
|
|
678
|
+
const tagDisplayNameProperties = ["x-displayName", "name"] as const;
|
|
679
|
+
|
|
680
|
+
export function getTagDisplayName(tagName: string, tags: TagObject[]): string {
|
|
681
|
+
// find the very own tagObject
|
|
682
|
+
const tagObject = tags.find((tagObject) => tagObject.name === tagName) ?? {
|
|
683
|
+
// if none found, just fake one
|
|
684
|
+
name: tagName,
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// return the first found and filled value from the property list
|
|
688
|
+
for (const property of tagDisplayNameProperties) {
|
|
689
|
+
const displayName = tagObject[property];
|
|
690
|
+
if (typeof displayName === "string") {
|
|
691
|
+
return displayName;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// always default to the tagName
|
|
696
|
+
return tagName;
|
|
697
|
+
}
|