Fix missing properties in allOf/oneOf (#3871)

This commit is contained in:
Nolann B.
2025-12-13 12:38:09 +01:00
committed by GitHub
parent 3e4e0b0bf0
commit 4766092ff9
2 changed files with 122 additions and 39 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@gitbook/react-openapi': patch
---
Fix missing properties in allOf/oneOf
+117 -39
View File
@@ -563,6 +563,75 @@ export function OpenAPISchemaPresentation(props: {
);
}
/**
* Process properties from a schema object into property entries.
*/
function processSchemaProperties(
schema: OpenAPIV3.SchemaObject,
discriminator?: OpenAPIV3.DiscriminatorObject | undefined,
discriminatorValue?: string | undefined,
allRequired?: Set<string>
): OpenAPISchemaPropertyEntry[] {
const result: OpenAPISchemaPropertyEntry[] = [];
if (schema.properties) {
Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => {
const isDiscriminator = discriminator?.propertyName === propertyName;
if (checkIsReference(propertySchema)) {
if (!isDiscriminator || !discriminatorValue) {
return;
}
}
let finalSchema = propertySchema;
if (isDiscriminator && discriminatorValue) {
finalSchema = {
...propertySchema,
const: discriminatorValue,
enum: [discriminatorValue],
};
}
result.push({
propertyName,
required: Array.isArray(schema.required)
? schema.required.includes(propertyName)
: allRequired?.has(propertyName)
? true
: undefined,
isDiscriminatorProperty: isDiscriminator,
schema: finalSchema,
});
});
}
if (schema.additionalProperties && !checkIsReference(schema.additionalProperties)) {
result.push({
propertyName: 'Other properties',
schema: schema.additionalProperties === true ? {} : schema.additionalProperties,
});
}
return result;
}
/**
* Merge properties into a result array, with later properties overriding earlier ones.
*/
function mergeProperties(
result: OpenAPISchemaPropertyEntry[],
newProperties: OpenAPISchemaPropertyEntry[]
): void {
for (const prop of newProperties) {
const existingIndex = result.findIndex((p) => p.propertyName === prop.propertyName);
if (existingIndex >= 0) {
result[existingIndex] = prop;
} else {
result.push(prop);
}
}
}
/**
* Get the sub-properties of a schema.
*/
@@ -596,46 +665,55 @@ function getSchemaProperties(
return [{ propertyName: 'items', schema: items }];
}
// Handle allOf by collecting properties from all schemas in the allOf
if (schema.allOf && Array.isArray(schema.allOf)) {
const allOfSchemas = schema.allOf.filter(
(s): s is OpenAPIV3.SchemaObject => !checkIsReference(s)
);
if (allOfSchemas.length > 0) {
const result: OpenAPISchemaPropertyEntry[] = [];
const allRequired = new Set<string>();
// Collect required fields and properties from all allOf schemas
for (const allOfSchema of allOfSchemas) {
if (Array.isArray(allOfSchema.required)) {
allOfSchema.required.forEach((req) => allRequired.add(req));
}
const allOfProperties = getSchemaProperties(
allOfSchema,
discriminator,
discriminatorValue
);
if (allOfProperties) {
mergeProperties(result, allOfProperties);
}
}
// Collect required fields from the schema itself
if (Array.isArray(schema.required)) {
schema.required.forEach((req) => allRequired.add(req));
}
// Include direct properties from the schema itself
mergeProperties(
result,
processSchemaProperties(schema, discriminator, discriminatorValue, allRequired)
);
// Update required status for all properties
result.forEach((prop) => {
if (prop.propertyName && allRequired.has(prop.propertyName)) {
prop.required = true;
}
});
return result.length > 0 ? result : null;
}
}
if (schema.type === 'object' || schema.properties) {
const result: OpenAPISchemaPropertyEntry[] = [];
if (schema.properties) {
Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => {
const isDiscriminator = discriminator?.propertyName === propertyName;
if (checkIsReference(propertySchema)) {
if (!isDiscriminator || !discriminatorValue) {
return;
}
}
let finalSchema = propertySchema;
if (isDiscriminator && discriminatorValue) {
finalSchema = {
...propertySchema,
const: discriminatorValue,
enum: [discriminatorValue],
};
}
result.push({
propertyName,
required: Array.isArray(schema.required)
? schema.required.includes(propertyName)
: undefined,
isDiscriminatorProperty: isDiscriminator,
schema: finalSchema,
});
});
}
if (schema.additionalProperties && !checkIsReference(schema.additionalProperties)) {
result.push({
propertyName: 'Other properties',
schema: schema.additionalProperties === true ? {} : schema.additionalProperties,
});
}
return result;
return processSchemaProperties(schema, discriminator, discriminatorValue);
}
return null;