fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #2556 from hey-api/fix--clean-up-required-array-after-read/write-split

fix: update schema pruning logic to correctly handle required properties

authored by

Lubos and committed by
GitHub
2693f682 54e9939e

+47 -14
+5
.changeset/seven-rings-drop.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + fix(parser): prune `required` array after removing properties
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts
··· 692 692 export const ModelWithPropertiesWritableSchema = { 693 693 description: 'This is a model with one nested property', 694 694 type: 'object', 695 - required: ['required', 'requiredAndReadOnly'], 695 + required: ['required'], 696 696 properties: { 697 697 required: { 698 698 type: 'string'
+3 -3
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/schemas/default/schemas.gen.ts
··· 1571 1571 1572 1572 export const ModelWithReadOnlyAndWriteOnlySchema = { 1573 1573 type: 'object', 1574 - required: ['foo', 'bar', 'baz'], 1574 + required: ['foo', 'bar'], 1575 1575 properties: { 1576 1576 foo: { 1577 1577 type: 'string' ··· 2002 2002 export const ModelWithPropertiesWritableSchema = { 2003 2003 description: 'This is a model with one nested property', 2004 2004 type: 'object', 2005 - required: ['required', 'requiredAndReadOnly', 'requiredAndNullable'], 2005 + required: ['required', 'requiredAndNullable'], 2006 2006 properties: { 2007 2007 required: { 2008 2008 type: 'string' ··· 2087 2087 2088 2088 export const ModelWithReadOnlyAndWriteOnlyWritableSchema = { 2089 2089 type: 'object', 2090 - required: ['foo', 'bar', 'baz'], 2090 + required: ['foo', 'baz'], 2091 2091 properties: { 2092 2092 foo: { 2093 2093 type: 'string'
+3 -3
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/schemas/default/schemas.gen.ts
··· 1564 1564 1565 1565 export const ModelWithReadOnlyAndWriteOnlySchema = { 1566 1566 type: 'object', 1567 - required: ['foo', 'bar', 'baz'], 1567 + required: ['foo', 'bar'], 1568 1568 properties: { 1569 1569 foo: { 1570 1570 type: 'string' ··· 1996 1996 export const ModelWithPropertiesWritableSchema = { 1997 1997 description: 'This is a model with one nested property', 1998 1998 type: 'object', 1999 - required: ['required', 'requiredAndReadOnly', 'requiredAndNullable'], 1999 + required: ['required', 'requiredAndNullable'], 2000 2000 properties: { 2001 2001 required: { 2002 2002 type: 'string' ··· 2080 2080 2081 2081 export const ModelWithReadOnlyAndWriteOnlyWritableSchema = { 2082 2082 type: 'object', 2083 - required: ['foo', 'bar', 'baz'], 2083 + required: ['foo', 'baz'], 2084 2084 properties: { 2085 2085 foo: { 2086 2086 type: 'string'
+35 -7
packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts
··· 139 139 scope: 'readOnly' | 'writeOnly', 140 140 ): boolean => { 141 141 if (schema && typeof schema === 'object') { 142 - // Remove $ref if the referenced schema is exclusively the excluded scope 143 - if ( 144 - '$ref' in schema && 145 - typeof (schema as Record<string, unknown>)['$ref'] === 'string' 146 - ) { 147 - const ref = (schema as Record<string, unknown>)['$ref'] as string; 148 - const nodeInfo = graph.nodes.get(ref); 142 + // Handle $ref schemas 143 + if ('$ref' in schema && typeof schema.$ref === 'string') { 144 + const nodeInfo = graph.nodes.get(schema.$ref); 149 145 if (nodeInfo?.scopes) { 150 146 // Only remove $ref if the referenced schema is *exclusively* the excluded scope. 151 147 // This ensures 'normal' or multi-scope schemas are always kept. ··· 197 193 !(value instanceof Array) 198 194 ) { 199 195 const objMap = value as Record<string, unknown>; 196 + // Track removed properties for object schemas to update required array 197 + const removedProperties = new Set<string>(); 198 + 200 199 for (const key of Object.keys(objMap)) { 201 200 const prop = objMap[key]; 202 201 if ( ··· 205 204 (prop as Record<string, unknown>)[scope] === true 206 205 ) { 207 206 delete objMap[key]; 207 + // Track removed properties for object schemas 208 + if (keyword === 'properties') { 209 + removedProperties.add(key); 210 + } 208 211 } else { 209 212 const shouldRemove = pruneSchemaByScope(graph, prop, scope); 210 213 if (shouldRemove) { 211 214 delete objMap[key]; 215 + // Track removed properties for object schemas 216 + if (keyword === 'properties') { 217 + removedProperties.add(key); 218 + } 212 219 } 213 220 } 214 221 } 222 + 223 + // Update required array if properties were removed 224 + if ( 225 + removedProperties.size > 0 && 226 + keyword === 'properties' && 227 + 'required' in schema && 228 + Array.isArray((schema as Record<string, unknown>).required) 229 + ) { 230 + const required = (schema as Record<string, unknown>) 231 + .required as string[]; 232 + const filteredRequired = required.filter( 233 + (prop) => !removedProperties.has(prop), 234 + ); 235 + 236 + if (filteredRequired.length === 0) { 237 + delete (schema as Record<string, unknown>).required; 238 + } else { 239 + (schema as Record<string, unknown>).required = filteredRequired; 240 + } 241 + } 242 + 215 243 if (!Object.keys(objMap).length) { 216 244 delete (schema as Record<string, unknown>)[keyword]; 217 245 }