Skip to content

Commit da485f5

Browse files
authored
Fix read-only in generateSchemaExample (#3069)
1 parent 139a805 commit da485f5

File tree

3 files changed

+109
-37
lines changed

3 files changed

+109
-37
lines changed

.changeset/short-toes-accept.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/react-openapi': patch
3+
---
4+
5+
Fix read-only in generateSchemaExample

packages/react-openapi/src/OpenAPIResponseExample.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ function getExamplesFromMediaTypeObject(args: {
268268
value: {
269269
[root]: generateSchemaExample(mediaTypeObject.schema, {
270270
xml: mediaType === 'application/xml',
271+
mode: 'read',
271272
}),
272273
},
273274
},
@@ -277,7 +278,11 @@ function getExamplesFromMediaTypeObject(args: {
277278
return [
278279
{
279280
key: 'default',
280-
example: { value: generateSchemaExample(mediaTypeObject.schema) },
281+
example: {
282+
value: generateSchemaExample(mediaTypeObject.schema, {
283+
mode: 'read',
284+
}),
285+
},
281286
},
282287
];
283288
}

packages/react-openapi/src/generateSchemaExample.ts

+98-36
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,10 @@ export function generateSchemaExample(
1616
schema: OpenAPIV3.SchemaObject,
1717
options?: GenerateSchemaExampleOptions
1818
): JSONValue | undefined {
19-
return getExampleFromSchema(
20-
schema,
21-
{
22-
emptyString: 'text',
23-
...options,
24-
},
25-
3 // Max depth for circular references
26-
);
19+
return getExampleFromSchema(schema, {
20+
emptyString: 'text',
21+
...options,
22+
});
2723
}
2824

2925
/**
@@ -103,21 +99,6 @@ function guessFromFormat(schema: Record<string, any>, fallback = '') {
10399
return genericExampleValues[schema.format] ?? fallback;
104100
}
105101

106-
/** Map of all the results */
107-
const resultCache = new WeakMap<Record<string, any>, any>();
108-
109-
/** Store result in the cache, and return the result */
110-
function cache(schema: Record<string, any>, result: unknown) {
111-
// Avoid unnecessary WeakMap operations for primitive values
112-
if (typeof result !== 'object' || result === null) {
113-
return result;
114-
}
115-
116-
resultCache.set(schema, result);
117-
118-
return result;
119-
}
120-
121102
/**
122103
* This function takes an OpenAPI schema and generates an example from it
123104
* Forked from : https://github.com/scalar/scalar/blob/main/packages/oas-utils/src/spec-getters/getExampleFromSchema.ts
@@ -152,8 +133,20 @@ const getExampleFromSchema = (
152133
},
153134
level = 0,
154135
parentSchema?: Record<string, any>,
155-
name?: string
136+
name?: string,
137+
resultCache = new WeakMap<Record<string, any>, any>()
156138
): any => {
139+
// Store result in the cache, and return the result
140+
function cache(schema: Record<string, any>, result: unknown) {
141+
// Avoid unnecessary WeakMap operations for primitive values
142+
if (typeof result !== 'object' || result === null) {
143+
return result;
144+
}
145+
146+
resultCache.set(schema, result);
147+
return result;
148+
}
149+
157150
// Check if the result is already cached
158151
if (resultCache.has(schema)) {
159152
return resultCache.get(schema);
@@ -245,7 +238,8 @@ const getExampleFromSchema = (
245238
options,
246239
level + 1,
247240
schema,
248-
propertyName
241+
propertyName,
242+
resultCache
249243
);
250244

251245
if (typeof response[propertyXmlTagName ?? propertyName] === 'undefined') {
@@ -269,7 +263,8 @@ const getExampleFromSchema = (
269263
options,
270264
level + 1,
271265
schema,
272-
exampleKey
266+
exampleKey,
267+
resultCache
273268
);
274269
}
275270
}
@@ -290,21 +285,51 @@ const getExampleFromSchema = (
290285
response.ANY_ADDITIONAL_PROPERTY = getExampleFromSchema(
291286
schema.additionalProperties,
292287
options,
293-
level + 1
288+
level + 1,
289+
undefined,
290+
undefined,
291+
resultCache
294292
);
295293
}
296294
}
297295

298296
if (schema.anyOf !== undefined) {
299-
Object.assign(response, getExampleFromSchema(schema.anyOf[0], options, level + 1));
297+
Object.assign(
298+
response,
299+
getExampleFromSchema(
300+
schema.anyOf[0],
301+
options,
302+
level + 1,
303+
undefined,
304+
undefined,
305+
resultCache
306+
)
307+
);
300308
} else if (schema.oneOf !== undefined) {
301-
Object.assign(response, getExampleFromSchema(schema.oneOf[0], options, level + 1));
309+
Object.assign(
310+
response,
311+
getExampleFromSchema(
312+
schema.oneOf[0],
313+
options,
314+
level + 1,
315+
undefined,
316+
undefined,
317+
resultCache
318+
)
319+
);
302320
} else if (schema.allOf !== undefined) {
303321
Object.assign(
304322
response,
305323
...schema.allOf
306324
.map((item: Record<string, any>) =>
307-
getExampleFromSchema(item, options, level + 1, schema)
325+
getExampleFromSchema(
326+
item,
327+
options,
328+
level + 1,
329+
schema,
330+
undefined,
331+
resultCache
332+
)
308333
)
309334
.filter((item: any) => item !== undefined)
310335
);
@@ -335,7 +360,9 @@ const getExampleFromSchema = (
335360
{ type: 'object', allOf: schema.items.allOf },
336361
options,
337362
level + 1,
338-
schema
363+
schema,
364+
undefined,
365+
resultCache
339366
);
340367

341368
return cache(
@@ -346,7 +373,14 @@ const getExampleFromSchema = (
346373
// For non-objects (like strings), collect all examples
347374
const examples = schema.items.allOf
348375
.map((item: Record<string, any>) =>
349-
getExampleFromSchema(item, options, level + 1, schema)
376+
getExampleFromSchema(
377+
item,
378+
options,
379+
level + 1,
380+
schema,
381+
undefined,
382+
resultCache
383+
)
350384
)
351385
.filter((item: any) => item !== undefined);
352386

@@ -368,7 +402,14 @@ const getExampleFromSchema = (
368402
const schemas = schema.items[rule].slice(0, 1);
369403
const exampleFromRule = schemas
370404
.map((item: Record<string, any>) =>
371-
getExampleFromSchema(item, options, level + 1, schema)
405+
getExampleFromSchema(
406+
item,
407+
options,
408+
level + 1,
409+
schema,
410+
undefined,
411+
resultCache
412+
)
372413
)
373414
.filter((item: any) => item !== undefined);
374415

@@ -380,7 +421,14 @@ const getExampleFromSchema = (
380421
}
381422

382423
if (schema.items?.type) {
383-
const exampleFromSchema = getExampleFromSchema(schema.items, options, level + 1);
424+
const exampleFromSchema = getExampleFromSchema(
425+
schema.items,
426+
options,
427+
level + 1,
428+
undefined,
429+
undefined,
430+
resultCache
431+
);
384432

385433
return wrapItems ? [{ [itemsXmlTagName]: exampleFromSchema }] : [exampleFromSchema];
386434
}
@@ -407,7 +455,14 @@ const getExampleFromSchema = (
407455
const firstOneOfItem = discriminateSchema[0];
408456

409457
// Return an example for the first item
410-
return getExampleFromSchema(firstOneOfItem, options, level + 1);
458+
return getExampleFromSchema(
459+
firstOneOfItem,
460+
options,
461+
level + 1,
462+
undefined,
463+
undefined,
464+
resultCache
465+
);
411466
}
412467

413468
// Check if schema has the `allOf` key
@@ -417,7 +472,14 @@ const getExampleFromSchema = (
417472
// Loop through all `allOf` schemas
418473
schema.allOf.forEach((allOfItem: Record<string, any>) => {
419474
// Return an example from the schema
420-
const newExample = getExampleFromSchema(allOfItem, options, level + 1);
475+
const newExample = getExampleFromSchema(
476+
allOfItem,
477+
options,
478+
level + 1,
479+
undefined,
480+
undefined,
481+
resultCache
482+
);
421483

422484
// Merge or overwrite the example
423485
example =

0 commit comments

Comments
 (0)