Fix read-only in generateSchemaExample (#3069)

This commit is contained in:
Nolann B.
2025-04-02 13:03:51 +02:00
committed by GitHub
parent 139a8050a5
commit da485f5144
3 changed files with 109 additions and 37 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@gitbook/react-openapi': patch
---
Fix read-only in generateSchemaExample
@@ -268,6 +268,7 @@ function getExamplesFromMediaTypeObject(args: {
value: { value: {
[root]: generateSchemaExample(mediaTypeObject.schema, { [root]: generateSchemaExample(mediaTypeObject.schema, {
xml: mediaType === 'application/xml', xml: mediaType === 'application/xml',
mode: 'read',
}), }),
}, },
}, },
@@ -277,7 +278,11 @@ function getExamplesFromMediaTypeObject(args: {
return [ return [
{ {
key: 'default', key: 'default',
example: { value: generateSchemaExample(mediaTypeObject.schema) }, example: {
value: generateSchemaExample(mediaTypeObject.schema, {
mode: 'read',
}),
},
}, },
]; ];
} }
@@ -16,14 +16,10 @@ export function generateSchemaExample(
schema: OpenAPIV3.SchemaObject, schema: OpenAPIV3.SchemaObject,
options?: GenerateSchemaExampleOptions options?: GenerateSchemaExampleOptions
): JSONValue | undefined { ): JSONValue | undefined {
return getExampleFromSchema( return getExampleFromSchema(schema, {
schema, emptyString: 'text',
{ ...options,
emptyString: 'text', });
...options,
},
3 // Max depth for circular references
);
} }
/** /**
@@ -103,21 +99,6 @@ function guessFromFormat(schema: Record<string, any>, fallback = '') {
return genericExampleValues[schema.format] ?? fallback; return genericExampleValues[schema.format] ?? fallback;
} }
/** Map of all the results */
const resultCache = new WeakMap<Record<string, any>, any>();
/** Store result in the cache, and return the result */
function cache(schema: Record<string, any>, result: unknown) {
// Avoid unnecessary WeakMap operations for primitive values
if (typeof result !== 'object' || result === null) {
return result;
}
resultCache.set(schema, result);
return result;
}
/** /**
* This function takes an OpenAPI schema and generates an example from it * This function takes an OpenAPI schema and generates an example from it
* Forked from : https://github.com/scalar/scalar/blob/main/packages/oas-utils/src/spec-getters/getExampleFromSchema.ts * Forked from : https://github.com/scalar/scalar/blob/main/packages/oas-utils/src/spec-getters/getExampleFromSchema.ts
@@ -152,8 +133,20 @@ const getExampleFromSchema = (
}, },
level = 0, level = 0,
parentSchema?: Record<string, any>, parentSchema?: Record<string, any>,
name?: string name?: string,
resultCache = new WeakMap<Record<string, any>, any>()
): any => { ): any => {
// Store result in the cache, and return the result
function cache(schema: Record<string, any>, result: unknown) {
// Avoid unnecessary WeakMap operations for primitive values
if (typeof result !== 'object' || result === null) {
return result;
}
resultCache.set(schema, result);
return result;
}
// Check if the result is already cached // Check if the result is already cached
if (resultCache.has(schema)) { if (resultCache.has(schema)) {
return resultCache.get(schema); return resultCache.get(schema);
@@ -245,7 +238,8 @@ const getExampleFromSchema = (
options, options,
level + 1, level + 1,
schema, schema,
propertyName propertyName,
resultCache
); );
if (typeof response[propertyXmlTagName ?? propertyName] === 'undefined') { if (typeof response[propertyXmlTagName ?? propertyName] === 'undefined') {
@@ -269,7 +263,8 @@ const getExampleFromSchema = (
options, options,
level + 1, level + 1,
schema, schema,
exampleKey exampleKey,
resultCache
); );
} }
} }
@@ -290,21 +285,51 @@ const getExampleFromSchema = (
response.ANY_ADDITIONAL_PROPERTY = getExampleFromSchema( response.ANY_ADDITIONAL_PROPERTY = getExampleFromSchema(
schema.additionalProperties, schema.additionalProperties,
options, options,
level + 1 level + 1,
undefined,
undefined,
resultCache
); );
} }
} }
if (schema.anyOf !== undefined) { if (schema.anyOf !== undefined) {
Object.assign(response, getExampleFromSchema(schema.anyOf[0], options, level + 1)); Object.assign(
response,
getExampleFromSchema(
schema.anyOf[0],
options,
level + 1,
undefined,
undefined,
resultCache
)
);
} else if (schema.oneOf !== undefined) { } else if (schema.oneOf !== undefined) {
Object.assign(response, getExampleFromSchema(schema.oneOf[0], options, level + 1)); Object.assign(
response,
getExampleFromSchema(
schema.oneOf[0],
options,
level + 1,
undefined,
undefined,
resultCache
)
);
} else if (schema.allOf !== undefined) { } else if (schema.allOf !== undefined) {
Object.assign( Object.assign(
response, response,
...schema.allOf ...schema.allOf
.map((item: Record<string, any>) => .map((item: Record<string, any>) =>
getExampleFromSchema(item, options, level + 1, schema) getExampleFromSchema(
item,
options,
level + 1,
schema,
undefined,
resultCache
)
) )
.filter((item: any) => item !== undefined) .filter((item: any) => item !== undefined)
); );
@@ -335,7 +360,9 @@ const getExampleFromSchema = (
{ type: 'object', allOf: schema.items.allOf }, { type: 'object', allOf: schema.items.allOf },
options, options,
level + 1, level + 1,
schema schema,
undefined,
resultCache
); );
return cache( return cache(
@@ -346,7 +373,14 @@ const getExampleFromSchema = (
// For non-objects (like strings), collect all examples // For non-objects (like strings), collect all examples
const examples = schema.items.allOf const examples = schema.items.allOf
.map((item: Record<string, any>) => .map((item: Record<string, any>) =>
getExampleFromSchema(item, options, level + 1, schema) getExampleFromSchema(
item,
options,
level + 1,
schema,
undefined,
resultCache
)
) )
.filter((item: any) => item !== undefined); .filter((item: any) => item !== undefined);
@@ -368,7 +402,14 @@ const getExampleFromSchema = (
const schemas = schema.items[rule].slice(0, 1); const schemas = schema.items[rule].slice(0, 1);
const exampleFromRule = schemas const exampleFromRule = schemas
.map((item: Record<string, any>) => .map((item: Record<string, any>) =>
getExampleFromSchema(item, options, level + 1, schema) getExampleFromSchema(
item,
options,
level + 1,
schema,
undefined,
resultCache
)
) )
.filter((item: any) => item !== undefined); .filter((item: any) => item !== undefined);
@@ -380,7 +421,14 @@ const getExampleFromSchema = (
} }
if (schema.items?.type) { if (schema.items?.type) {
const exampleFromSchema = getExampleFromSchema(schema.items, options, level + 1); const exampleFromSchema = getExampleFromSchema(
schema.items,
options,
level + 1,
undefined,
undefined,
resultCache
);
return wrapItems ? [{ [itemsXmlTagName]: exampleFromSchema }] : [exampleFromSchema]; return wrapItems ? [{ [itemsXmlTagName]: exampleFromSchema }] : [exampleFromSchema];
} }
@@ -407,7 +455,14 @@ const getExampleFromSchema = (
const firstOneOfItem = discriminateSchema[0]; const firstOneOfItem = discriminateSchema[0];
// Return an example for the first item // Return an example for the first item
return getExampleFromSchema(firstOneOfItem, options, level + 1); return getExampleFromSchema(
firstOneOfItem,
options,
level + 1,
undefined,
undefined,
resultCache
);
} }
// Check if schema has the `allOf` key // Check if schema has the `allOf` key
@@ -417,7 +472,14 @@ const getExampleFromSchema = (
// Loop through all `allOf` schemas // Loop through all `allOf` schemas
schema.allOf.forEach((allOfItem: Record<string, any>) => { schema.allOf.forEach((allOfItem: Record<string, any>) => {
// Return an example from the schema // Return an example from the schema
const newExample = getExampleFromSchema(allOfItem, options, level + 1); const newExample = getExampleFromSchema(
allOfItem,
options,
level + 1,
undefined,
undefined,
resultCache
);
// Merge or overwrite the example // Merge or overwrite the example
example = example =