From 26252cbaa0a2f67a19e1476c719c3ae232c88542 Mon Sep 17 00:00:00 2001 From: carson2222 Date: Fri, 29 Aug 2025 15:59:37 +0200 Subject: [PATCH 1/3] fix: update schema pruning logic to correctly handle required properties --- .../@hey-api/schemas/default/schemas.gen.ts | 2 +- .../@hey-api/schemas/default/schemas.gen.ts | 6 +-- .../@hey-api/schemas/default/schemas.gen.ts | 6 +-- .../openApi/shared/transforms/readWrite.ts | 42 +++++++++++++++---- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts index 6c4d40ce41..466f10c456 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts @@ -692,7 +692,7 @@ export const failure_FailureSchema = { export const ModelWithPropertiesWritableSchema = { description: 'This is a model with one nested property', type: 'object', - required: ['required', 'requiredAndReadOnly'], + required: ['required'], properties: { required: { type: 'string' diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts index 1e18daa3fc..f2b3eaf053 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts @@ -1571,7 +1571,7 @@ export const ModelWithNestedCompositionEnumsSchema = { export const ModelWithReadOnlyAndWriteOnlySchema = { type: 'object', - required: ['foo', 'bar', 'baz'], + required: ['foo', 'bar'], properties: { foo: { type: 'string' @@ -2002,7 +2002,7 @@ export const Generic_Schema_Duplicate_Issue_1_System_String_Schema = { export const ModelWithPropertiesWritableSchema = { description: 'This is a model with one nested property', type: 'object', - required: ['required', 'requiredAndReadOnly', 'requiredAndNullable'], + required: ['required', 'requiredAndNullable'], properties: { required: { type: 'string' @@ -2087,7 +2087,7 @@ export const FileWritableSchema = { export const ModelWithReadOnlyAndWriteOnlyWritableSchema = { type: 'object', - required: ['foo', 'bar', 'baz'], + required: ['foo', 'baz'], properties: { foo: { type: 'string' diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/schemas/default/schemas.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/schemas/default/schemas.gen.ts index 34a76b8801..4e3603d9c2 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/schemas/default/schemas.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/schemas/default/schemas.gen.ts @@ -1564,7 +1564,7 @@ export const ModelWithNestedCompositionEnumsSchema = { export const ModelWithReadOnlyAndWriteOnlySchema = { type: 'object', - required: ['foo', 'bar', 'baz'], + required: ['foo', 'bar'], properties: { foo: { type: 'string' @@ -1996,7 +1996,7 @@ export const Generic_Schema_Duplicate_Issue_1_System_String_Schema = { export const ModelWithPropertiesWritableSchema = { description: 'This is a model with one nested property', type: 'object', - required: ['required', 'requiredAndReadOnly', 'requiredAndNullable'], + required: ['required', 'requiredAndNullable'], properties: { required: { type: 'string' @@ -2080,7 +2080,7 @@ export const FileWritableSchema = { export const ModelWithReadOnlyAndWriteOnlyWritableSchema = { type: 'object', - required: ['foo', 'bar', 'baz'], + required: ['foo', 'baz'], properties: { foo: { type: 'string' diff --git a/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts b/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts index caf72e6f6a..884c0def1e 100644 --- a/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts +++ b/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts @@ -139,13 +139,9 @@ const pruneSchemaByScope = ( scope: 'readOnly' | 'writeOnly', ): boolean => { if (schema && typeof schema === 'object') { - // Remove $ref if the referenced schema is exclusively the excluded scope - if ( - '$ref' in schema && - typeof (schema as Record)['$ref'] === 'string' - ) { - const ref = (schema as Record)['$ref'] as string; - const nodeInfo = graph.nodes.get(ref); + // Handle $ref schemas + if ('$ref' in schema && typeof schema.$ref === 'string') { + const nodeInfo = graph.nodes.get(schema.$ref); if (nodeInfo?.scopes) { // Only remove $ref if the referenced schema is *exclusively* the excluded scope. // This ensures 'normal' or multi-scope schemas are always kept. @@ -197,6 +193,9 @@ const pruneSchemaByScope = ( !(value instanceof Array) ) { const objMap = value as Record; + // Track removed properties for object schemas to update required array + const removedProperties = new Set(); + for (const key of Object.keys(objMap)) { const prop = objMap[key]; if ( @@ -205,13 +204,42 @@ const pruneSchemaByScope = ( (prop as Record)[scope] === true ) { delete objMap[key]; + // Track removed properties for object schemas + if (keyword === 'properties') { + removedProperties.add(key); + } } else { const shouldRemove = pruneSchemaByScope(graph, prop, scope); if (shouldRemove) { delete objMap[key]; + // Track removed properties for object schemas + if (keyword === 'properties') { + removedProperties.add(key); + } } } } + + // Update required array if properties were removed + if ( + removedProperties.size > 0 && + keyword === 'properties' && + 'required' in schema && + Array.isArray((schema as Record).required) + ) { + const required = (schema as Record) + .required as string[]; + const filteredRequired = required.filter( + (prop) => !removedProperties.has(prop), + ); + + if (filteredRequired.length === 0) { + delete (schema as Record).required; + } else { + (schema as Record).required = filteredRequired; + } + } + if (!Object.keys(objMap).length) { delete (schema as Record)[keyword]; } From 74c16f219706d22aebbc166e8bbb95412a2a5b6f Mon Sep 17 00:00:00 2001 From: carson2222 Date: Fri, 29 Aug 2025 16:13:05 +0200 Subject: [PATCH 2/3] chore: update changeset --- .changeset/seven-rings-drop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/seven-rings-drop.md diff --git a/.changeset/seven-rings-drop.md b/.changeset/seven-rings-drop.md new file mode 100644 index 0000000000..fc7508bade --- /dev/null +++ b/.changeset/seven-rings-drop.md @@ -0,0 +1,5 @@ +--- +'@hey-api/openapi-ts': patch +--- + +Update schema pruning logic to correctly handle required properties From e93eeec751e32e2d260cf29477c089f0c2bb99b5 Mon Sep 17 00:00:00 2001 From: Lubos Date: Sat, 30 Aug 2025 06:05:17 +0800 Subject: [PATCH 3/3] Update seven-rings-drop.md --- .changeset/seven-rings-drop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/seven-rings-drop.md b/.changeset/seven-rings-drop.md index fc7508bade..e74438eef0 100644 --- a/.changeset/seven-rings-drop.md +++ b/.changeset/seven-rings-drop.md @@ -2,4 +2,4 @@ '@hey-api/openapi-ts': patch --- -Update schema pruning logic to correctly handle required properties +fix(parser): prune `required` array after removing properties