@xyd-js/openapi 0.1.0-build.168
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/CHANGELOG.md +1517 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/__fixtures__/-2.complex.openai/input.yaml +39848 -0
- package/__fixtures__/-2.complex.openai/output.json +321646 -0
- package/__fixtures__/-2.complex.openai/pluginOasOpenai.ts +553 -0
- package/__fixtures__/-3.random/input.yaml +234 -0
- package/__fixtures__/-3.random/output.json +1140 -0
- package/__fixtures__/1.basic/input.yaml +226 -0
- package/__fixtures__/1.basic/output.json +1919 -0
- package/__fixtures__/2.more/input.yaml +76 -0
- package/__fixtures__/2.more/output.json +327 -0
- package/__fixtures__/3.multiple-responses/input.yaml +48 -0
- package/__fixtures__/3.multiple-responses/output.json +311 -0
- package/__fixtures__/5.xdocs.codeLanguages/input.yaml +231 -0
- package/__fixtures__/5.xdocs.codeLanguages/output.json +1879 -0
- package/__fixtures__/5.xdocs.sidebar/input.yaml +256 -0
- package/__fixtures__/5.xdocs.sidebar/output.json +843 -0
- package/__fixtures__/6.codeSamples/input.yaml +75 -0
- package/__fixtures__/6.codeSamples/output.json +293 -0
- package/__tests__/oapSchemaToReferences.test.ts +82 -0
- package/__tests__/utils.ts +81 -0
- package/dist/index.cjs +2154 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +2119 -0
- package/dist/index.js.map +1 -0
- package/examples/basic/index.ts +20 -0
- package/examples/basic/index2.ts +36 -0
- package/examples/basic/openapi.yaml +124 -0
- package/examples/dist/index.cjs +2 -0
- package/examples/dist/index.cjs.map +1 -0
- package/examples/dist/index.d.cts +2 -0
- package/examples/dist/index.d.ts +2 -0
- package/examples/dist/index.js +2 -0
- package/examples/dist/index.js.map +1 -0
- package/examples/semi/index.ts +16 -0
- package/examples/semi/openapi.yaml +365 -0
- package/examples/semi/references.json +500 -0
- package/examples/webhooks/index.ts +16 -0
- package/examples/webhooks/openapi.yaml +248 -0
- package/examples/webhooks/references.json +895 -0
- package/index.ts +12 -0
- package/package.json +31 -0
- package/src/const.ts +14 -0
- package/src/converters/oas-componentSchemas.ts +205 -0
- package/src/converters/oas-examples.ts +530 -0
- package/src/converters/oas-parameters.ts +41 -0
- package/src/converters/oas-paths.ts +354 -0
- package/src/converters/oas-requestBody.ts +57 -0
- package/src/converters/oas-responses.ts +76 -0
- package/src/converters/oas-schema.ts +141 -0
- package/src/index.ts +21 -0
- package/src/oas-core.ts +579 -0
- package/src/types.ts +18 -0
- package/src/utils.ts +157 -0
- package/src/xdocs/index.ts +18 -0
- package/src/xdocs/pluginSidebar.ts +580 -0
- package/src/xdocs/types.ts +26 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +19 -0
- package/tsup.examples-config.ts +30 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import {OpenAPIV3} from "openapi-types";
|
|
2
|
+
import Oas from "oas";
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import {Operation} from 'oas/operation'; // TODO: fix ts
|
|
5
|
+
import oasToSnippet from "@readme/oas-to-snippet";
|
|
6
|
+
import {JSONSchema, sample as openApiSampler} from '@xyd-js/openapi-sampler';
|
|
7
|
+
import {JSONSchema7, JSONSchema7Definition} from "json-schema";
|
|
8
|
+
|
|
9
|
+
import {ExampleGroup, Example, CodeBlockTab} from "@xyd-js/uniform";
|
|
10
|
+
|
|
11
|
+
import {BUILT_IN_PROPERTIES} from "../const";
|
|
12
|
+
import {xDocsLanguages} from "../xdocs";
|
|
13
|
+
|
|
14
|
+
// TODO: custom snippet languages options
|
|
15
|
+
const DEFAULT_CODE_LANGUAGES = ["shell", "javascript", "python", "go"]
|
|
16
|
+
|
|
17
|
+
// TODO: option with another languages
|
|
18
|
+
export function oapExamples(
|
|
19
|
+
oas: Oas,
|
|
20
|
+
operation: Operation,
|
|
21
|
+
visitedExamples?: Map<JSONSchema7 | JSONSchema7[], any>
|
|
22
|
+
): ExampleGroup[] {
|
|
23
|
+
const exampleGroups = [
|
|
24
|
+
...reqExamples(operation, oas, visitedExamples),
|
|
25
|
+
...resBodyExmaples(operation, oas, visitedExamples),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
return exampleGroups
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function langFallback(lang: string): string {
|
|
32
|
+
const langLower = lang.toLowerCase()
|
|
33
|
+
|
|
34
|
+
switch (langLower) {
|
|
35
|
+
case "curl": {
|
|
36
|
+
return "shell";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return langLower;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function smartDeepCopy<T>(obj: T, excludeProps: string[] = []): T {
|
|
44
|
+
const seen = new WeakMap();
|
|
45
|
+
|
|
46
|
+
function copy(value: any): any {
|
|
47
|
+
// Handle primitives and null
|
|
48
|
+
if (value === null || typeof value !== 'object') {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Handle arrays
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
return value.map(copy);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle dates
|
|
58
|
+
if (value instanceof Date) {
|
|
59
|
+
return new Date(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check for circular references
|
|
63
|
+
if (seen.has(value)) {
|
|
64
|
+
return seen.get(value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Create new object
|
|
68
|
+
const result: any = {};
|
|
69
|
+
seen.set(value, result);
|
|
70
|
+
|
|
71
|
+
// Copy all properties except excluded ones
|
|
72
|
+
for (const [key, val] of Object.entries(value)) {
|
|
73
|
+
const propPath = key;
|
|
74
|
+
if (!excludeProps.some(prop => propPath.startsWith(prop))) {
|
|
75
|
+
result[key] = copy(val);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return copy(obj);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function excludeProperties(operation: Operation, excludeProps: string[]): Operation {
|
|
86
|
+
return smartDeepCopy(operation, excludeProps);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function reqExamples(operation: Operation, oas: Oas, vistedExamples?: Map<JSONSchema7 | JSONSchema7[], any>): ExampleGroup[] {
|
|
90
|
+
const exampleGroups: ExampleGroup[] = []
|
|
91
|
+
const examples: Example[] = []
|
|
92
|
+
const tabs: CodeBlockTab[] = []
|
|
93
|
+
|
|
94
|
+
// Handle x-codeSamples if present
|
|
95
|
+
if (operation.schema['x-codeSamples']) {
|
|
96
|
+
const codeSamples = operation.schema['x-codeSamples'] as Array<{ lang: string; source: string; label?: string }>
|
|
97
|
+
|
|
98
|
+
// Group code samples by label
|
|
99
|
+
const groupedByLabel = new Map<string, Array<{ lang: string; source: string; label?: string }>>()
|
|
100
|
+
|
|
101
|
+
codeSamples.forEach(sample => {
|
|
102
|
+
const label = sample.label || 'default'
|
|
103
|
+
if (!groupedByLabel.has(label)) {
|
|
104
|
+
groupedByLabel.set(label, [])
|
|
105
|
+
}
|
|
106
|
+
groupedByLabel.get(label)!.push(sample)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Create examples for each label group
|
|
110
|
+
groupedByLabel.forEach((samples, label) => {
|
|
111
|
+
const codeSampleTabs: CodeBlockTab[] = samples.map(sample => ({
|
|
112
|
+
title: sample.lang,
|
|
113
|
+
language: langFallback(sample.lang),
|
|
114
|
+
code: sample.source
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
if (codeSampleTabs.length > 0) {
|
|
118
|
+
examples.push({
|
|
119
|
+
codeblock: {
|
|
120
|
+
title: label === 'default' ? "Example request" : label,
|
|
121
|
+
tabs: codeSampleTabs
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (examples.length > 0) {
|
|
128
|
+
exampleGroups.push({
|
|
129
|
+
description: "Example request",
|
|
130
|
+
examples
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
return exampleGroups
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Create a single object with all parameters grouped by their location
|
|
138
|
+
const paramData = operation.schema.parameters
|
|
139
|
+
? (operation.schema.parameters as OpenAPIV3.ParameterObject[]).reduce((acc, param) => {
|
|
140
|
+
const location = param.in || 'query'
|
|
141
|
+
if (!acc[location]) {
|
|
142
|
+
acc[location] = {}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let value = param.example
|
|
146
|
+
if (!value && param.schema) {
|
|
147
|
+
value = openApiSampler(sanitizeSchema(param.schema as JSONSchema7))
|
|
148
|
+
}
|
|
149
|
+
if (value !== undefined) {
|
|
150
|
+
acc[location][param.name] = value
|
|
151
|
+
}
|
|
152
|
+
return acc
|
|
153
|
+
}, {} as Record<string, Record<string, any>>)
|
|
154
|
+
: {}
|
|
155
|
+
|
|
156
|
+
// Get request body data if it exists
|
|
157
|
+
let bodyData = {}
|
|
158
|
+
if (operation.schema.requestBody) {
|
|
159
|
+
const body = operation.schema.requestBody as OpenAPIV3.RequestBodyObject
|
|
160
|
+
const contentTypes = Object.keys(body.content)
|
|
161
|
+
|
|
162
|
+
if (contentTypes.length > 0) {
|
|
163
|
+
const contentType = contentTypes[contentTypes.length - 1]
|
|
164
|
+
const content = body.content[contentType]
|
|
165
|
+
let schema = content?.schema as JSONSchema7
|
|
166
|
+
|
|
167
|
+
if (schema) {
|
|
168
|
+
schema = fixAllOfBug(schema)
|
|
169
|
+
schema = sanitizeSchema(schema)
|
|
170
|
+
|
|
171
|
+
let requestData
|
|
172
|
+
if (content.examples) {
|
|
173
|
+
const requestExample = content.examples["request"]
|
|
174
|
+
if (requestExample && "value" in requestExample) {
|
|
175
|
+
requestData = requestExample.value
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!requestData) {
|
|
180
|
+
requestData = sampleFromSchema(schema)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (contentType === 'application/x-www-form-urlencoded') {
|
|
184
|
+
bodyData = {formData: requestData}
|
|
185
|
+
} else {
|
|
186
|
+
bodyData = {body: requestData}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check if we have parameters or request body
|
|
193
|
+
const hasRequestBody = operation.schema.requestBody !== undefined
|
|
194
|
+
const hasParameters = Object.keys(paramData).length > 0
|
|
195
|
+
|
|
196
|
+
// Generate examples if we have either parameters or request body, or if we have neither
|
|
197
|
+
if (hasParameters || hasRequestBody || (!hasRequestBody && !hasParameters)) {
|
|
198
|
+
const langs = xDocsLanguages(operation.api) || DEFAULT_CODE_LANGUAGES
|
|
199
|
+
langs.forEach(lang => {
|
|
200
|
+
// Sanitize operation to remove circular references before passing to oasToSnippet
|
|
201
|
+
let snippetOperation = operation
|
|
202
|
+
const v = operation?.api?.paths?.[operation?.path]?.[operation?.method]
|
|
203
|
+
if (v) {
|
|
204
|
+
snippetOperation = fixCircularReferences(v);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const {code} = oasToSnippet(oas, snippetOperation, {
|
|
208
|
+
...paramData,
|
|
209
|
+
...bodyData
|
|
210
|
+
}, null, lang)
|
|
211
|
+
|
|
212
|
+
tabs.push({
|
|
213
|
+
title: lang,
|
|
214
|
+
language: lang,
|
|
215
|
+
code: code || ""
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
if (tabs.length > 0) {
|
|
220
|
+
examples.push({
|
|
221
|
+
codeblock: {
|
|
222
|
+
tabs
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (examples.length > 0) {
|
|
228
|
+
exampleGroups.push({
|
|
229
|
+
description: "Example request",
|
|
230
|
+
examples
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return exampleGroups
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function resBodyExmaples(operation: Operation, oas: Oas, vistedExamples?: Map<JSONSchema7 | JSONSchema7[], any>): ExampleGroup[] {
|
|
239
|
+
const exampleGroups: ExampleGroup[] = []
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if (operation.schema.responses) {
|
|
243
|
+
const responses = operation.schema.responses as OpenAPIV3.ResponsesObject
|
|
244
|
+
|
|
245
|
+
const examples: Example[] = []
|
|
246
|
+
|
|
247
|
+
Object.entries(responses).forEach(([status, r]) => {
|
|
248
|
+
const response = r as OpenAPIV3.ResponseObject
|
|
249
|
+
if (!response.content) {
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const contentTypes = Object.keys(response.content)
|
|
254
|
+
if (contentTypes.length === 0) {
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const tabs: CodeBlockTab[] = []
|
|
259
|
+
|
|
260
|
+
for (const contentType of contentTypes) {
|
|
261
|
+
const content = response.content[contentType]
|
|
262
|
+
const schema = content?.schema as JSONSchema7
|
|
263
|
+
|
|
264
|
+
if (!schema) {
|
|
265
|
+
continue
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let responseData
|
|
269
|
+
// Check for examples in the response content
|
|
270
|
+
if (content.examples) {
|
|
271
|
+
const responseExample = content.examples["response"]
|
|
272
|
+
if (responseExample && "value" in responseExample) {
|
|
273
|
+
responseData = responseExample.value
|
|
274
|
+
} else {
|
|
275
|
+
const namedExamples: Example[] = []
|
|
276
|
+
const exampleNames = Object.keys(content.examples)
|
|
277
|
+
|
|
278
|
+
exampleNames.forEach((exampleName) => {
|
|
279
|
+
const data = content?.examples?.[exampleName]
|
|
280
|
+
|
|
281
|
+
if (!data || !("value" in data) || typeof data.value != "object") {
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
namedExamples.push({
|
|
286
|
+
description: "",
|
|
287
|
+
codeblock: {
|
|
288
|
+
title: exampleName,
|
|
289
|
+
tabs: [
|
|
290
|
+
{
|
|
291
|
+
title: "application/json", // TODO: support multiple types
|
|
292
|
+
language: "json",
|
|
293
|
+
code: JSON.stringify(data.value, null, 2) || "",
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
if (namedExamples.length === 1) {
|
|
301
|
+
const firstCodeblock = namedExamples[0].codeblock
|
|
302
|
+
|
|
303
|
+
tabs.push(
|
|
304
|
+
...firstCodeblock.tabs.map(tab => ({
|
|
305
|
+
...tab,
|
|
306
|
+
title: contentType
|
|
307
|
+
}))
|
|
308
|
+
)
|
|
309
|
+
} else {
|
|
310
|
+
exampleGroups.push({
|
|
311
|
+
description: "",
|
|
312
|
+
examples: namedExamples
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
continue
|
|
317
|
+
}
|
|
318
|
+
} else if (content.example) {
|
|
319
|
+
responseData = content.example
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// If no example found, generate sample data from schema
|
|
323
|
+
if (!responseData) {
|
|
324
|
+
responseData = sampleFromSchema(schema)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
let extension = "text"
|
|
328
|
+
switch (contentType) {
|
|
329
|
+
case "application/json":
|
|
330
|
+
case "application/problem+json":
|
|
331
|
+
case "application/vnd.api+json": {
|
|
332
|
+
extension = "json"
|
|
333
|
+
break
|
|
334
|
+
}
|
|
335
|
+
case "application/xml":
|
|
336
|
+
case "text/xml":
|
|
337
|
+
case "application/problem+xml": {
|
|
338
|
+
extension = "xml"
|
|
339
|
+
break
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
tabs.push({
|
|
344
|
+
title: contentType,
|
|
345
|
+
language: extension,
|
|
346
|
+
code: JSON.stringify(responseData, null, 2) || "",
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (tabs.length > 0) {
|
|
351
|
+
examples.push({
|
|
352
|
+
codeblock: {
|
|
353
|
+
title: status,
|
|
354
|
+
tabs
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
if (examples.length > 0) {
|
|
361
|
+
exampleGroups.push({
|
|
362
|
+
description: "Example response",
|
|
363
|
+
examples
|
|
364
|
+
})
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return exampleGroups
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* fixAllOfBug fixes below case:
|
|
373
|
+
*
|
|
374
|
+
* ```yaml
|
|
375
|
+
* allOf:
|
|
376
|
+
* - $ref: '#/components/schemas/SomeSchema'
|
|
377
|
+
* - type: object
|
|
378
|
+
* required:
|
|
379
|
+
* properties:
|
|
380
|
+
* ```
|
|
381
|
+
*
|
|
382
|
+
*/
|
|
383
|
+
function fixAllOfBug(schema: JSONSchema7) {
|
|
384
|
+
const modifiedSchema = {...schema}
|
|
385
|
+
|
|
386
|
+
if (schema?.allOf) {
|
|
387
|
+
schema.allOf.forEach((prop, i) => {
|
|
388
|
+
const propObj = prop as object
|
|
389
|
+
|
|
390
|
+
if ("properties" in propObj && !propObj["properties"]) {
|
|
391
|
+
delete modifiedSchema.allOf?.[i]
|
|
392
|
+
}
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return modifiedSchema
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
function sanitizeSchema(
|
|
401
|
+
schema: any,
|
|
402
|
+
vistedExamples: Map<JSONSchema7 | JSONSchema7[], any> = new Map(),
|
|
403
|
+
parent?: any
|
|
404
|
+
): any {
|
|
405
|
+
if (vistedExamples.has(schema)) {
|
|
406
|
+
const cached = vistedExamples.get(schema);
|
|
407
|
+
|
|
408
|
+
if (typeof cached === 'object') {
|
|
409
|
+
return JSON.parse(JSON.stringify(cached)); // Return a deep copy of the cached schema
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return cached
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (parent) {
|
|
416
|
+
vistedExamples.set(schema, parent);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (!schema || typeof schema !== 'object') {
|
|
420
|
+
vistedExamples.set(schema, schema);
|
|
421
|
+
return schema;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (Array.isArray(schema)) {
|
|
425
|
+
const v = schema.map(item => sanitizeSchema(item, vistedExamples));
|
|
426
|
+
vistedExamples.set(schema, v);
|
|
427
|
+
return v;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const cleaned: any = {};
|
|
431
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
432
|
+
if (key === "__UNSAFE_refPath") {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (!BUILT_IN_PROPERTIES[key]) {
|
|
436
|
+
cleaned[key] = sanitizeSchema(value, vistedExamples, cleaned);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
vistedExamples.set(schema, cleaned);
|
|
440
|
+
return cleaned;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function sampleFromSchema(
|
|
444
|
+
schema: JSONSchema7
|
|
445
|
+
) {
|
|
446
|
+
let jsonSchema: JSONSchema | null = null
|
|
447
|
+
|
|
448
|
+
let multiSpec: JSONSchema7Definition[] | null = null
|
|
449
|
+
|
|
450
|
+
if (schema.oneOf?.length) {
|
|
451
|
+
// for one of schemas, we take from the last one
|
|
452
|
+
multiSpec = schema.oneOf
|
|
453
|
+
} else if (schema.anyOf?.length) {
|
|
454
|
+
// for any of schemas, we take from the last one
|
|
455
|
+
multiSpec = schema.anyOf
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (multiSpec?.length) {
|
|
459
|
+
// for one of schemas, we take from the last one
|
|
460
|
+
for (let i = multiSpec.length - 1; i >= 0; i--) {
|
|
461
|
+
const spec = multiSpec[i];
|
|
462
|
+
const sanitized = sanitizeSchema(spec)
|
|
463
|
+
if (!sanitized) {
|
|
464
|
+
continue
|
|
465
|
+
}
|
|
466
|
+
jsonSchema = sanitized
|
|
467
|
+
|
|
468
|
+
if (!jsonSchema?.properties) {
|
|
469
|
+
continue
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
jsonSchema = sanitizeSchema(schema)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (jsonSchema) {
|
|
478
|
+
return openApiSampler(jsonSchema)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return null
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function fixCircularReferences(schema: any, visited: WeakMap<any, any> = new WeakMap()): any {
|
|
485
|
+
if (!schema || typeof schema !== 'object') {
|
|
486
|
+
return schema;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Check if we've already processed this object to prevent infinite recursion
|
|
490
|
+
if (visited.has(schema)) {
|
|
491
|
+
return visited.get(schema);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Check if this schema has circular references
|
|
495
|
+
if ((schema as any).__UNSAFE_circular) {
|
|
496
|
+
// Return a simplified version without circular references
|
|
497
|
+
const simplified = {
|
|
498
|
+
type: 'object',
|
|
499
|
+
description: 'Circular reference detected - schema simplified'
|
|
500
|
+
};
|
|
501
|
+
visited.set(schema, simplified);
|
|
502
|
+
return simplified;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Handle arrays
|
|
506
|
+
if (Array.isArray(schema)) {
|
|
507
|
+
const result = schema.map(item => fixCircularReferences(item, visited));
|
|
508
|
+
visited.set(schema, result);
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Recursively fix circular references in nested objects
|
|
513
|
+
const fixedSchema: any = {};
|
|
514
|
+
visited.set(schema, fixedSchema); // Set early to prevent infinite recursion
|
|
515
|
+
|
|
516
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
517
|
+
if (key === '__UNSAFE_circular' || key === '__UNSAFE_refPath') {
|
|
518
|
+
continue; // Skip unsafe properties
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (typeof value === 'object' && value !== null) {
|
|
522
|
+
fixedSchema[key] = fixCircularReferences(value, visited);
|
|
523
|
+
} else {
|
|
524
|
+
fixedSchema[key] = value;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return fixedSchema;
|
|
529
|
+
}
|
|
530
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {OpenAPIV3} from "openapi-types";
|
|
2
|
+
|
|
3
|
+
import {DefinitionProperty} from "@xyd-js/uniform";
|
|
4
|
+
|
|
5
|
+
import { schemaObjectToUniformDefinitionPropertyMeta } from "../oas-core";
|
|
6
|
+
|
|
7
|
+
// oapParametersToDefinitionProperties converts OpenAPI parameters to uniform DefinitionProperties
|
|
8
|
+
export function oapParametersToDefinitionProperties(
|
|
9
|
+
parameters: OpenAPIV3.ParameterObject[]
|
|
10
|
+
): { [key: string]: DefinitionProperty[] } {
|
|
11
|
+
const parameterIn: { [key: string]: DefinitionProperty[] } = {}
|
|
12
|
+
|
|
13
|
+
parameters.forEach((param) => {
|
|
14
|
+
if (!parameterIn[param.in]) {
|
|
15
|
+
parameterIn[param.in] = []
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const schema = param.schema as OpenAPIV3.SchemaObject
|
|
19
|
+
|
|
20
|
+
const meta = [
|
|
21
|
+
...(schemaObjectToUniformDefinitionPropertyMeta(schema, param.name) || []),
|
|
22
|
+
...(schemaObjectToUniformDefinitionPropertyMeta(param, param.name) || []),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
let oapV2Type = ""
|
|
26
|
+
if ("type" in param) {
|
|
27
|
+
oapV2Type = param.type as string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const property: DefinitionProperty = {
|
|
31
|
+
name: param.name,
|
|
32
|
+
type: schema?.type || oapV2Type || "",
|
|
33
|
+
description: param.description || "",
|
|
34
|
+
meta
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
parameterIn[param.in].push(property)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return parameterIn
|
|
41
|
+
}
|