@@ -214,12 +214,17 @@ export const calculateSpecRefs = (sourceDocument: unknown, normalizedSpec: unkno
214214 return
215215 }
216216 const componentName = matchResult . grepValues [ grepKey ] . toString ( )
217- let sourceComponents = getKeyValue ( sourceDocument , ...matchResult . path ) as unknown
217+ let sourceComponents = getKeyValue ( sourceDocument , ...matchResult . path )
218218 if ( ! sourceComponents || typeof sourceComponents !== 'object' ) {
219219 return
220220 }
221- sourceComponents = prunePathItemMethods ( sourceComponents , resultSpec , matchResult . path )
222- // component is defined and object at this point
221+
222+ if ( typeof sourceComponents === 'object' ) {
223+ const allowedOps = getAllowedHttpOps ( resultSpec , matchResult . path )
224+ if ( allowedOps . length > 0 && isComponentsPathItemRef ( matchResult . path ) ) {
225+ sourceComponents = filterPathItemOperations ( sourceComponents , allowedOps )
226+ }
227+ }
223228 if ( models && ! models [ componentName ] && isComponentsSchemaRef ( matchResult . path ) ) {
224229 const existingHash = componentsHashMap ?. get ( componentName )
225230 if ( existingHash ) {
@@ -247,41 +252,22 @@ export const isComponentsPathItemRef = (path: JsonPath): boolean => {
247252 )
248253}
249254
250- /**
251- * Returns a shallow-copied object with HTTP method keys pruned to the allowed set,
252- * but only when the jsonPath points to components.pathItems.
253- *
254- * If jsonPath does not match components.pathItems or allowedOps is empty, returns input unchanged.
255- */
256- export const prunePathItemMethods = (
255+ export const filterPathItemOperations = (
257256 source : unknown ,
258- resultSpec : unknown ,
259- jsonPath : JsonPath ,
257+ allowedMethods : string [ ] ,
260258) : unknown => {
261- const allowedOps = getAllowedHttpOps ( resultSpec , jsonPath )
262- if ( ! source || typeof source !== 'object' ) {
263- return source
264- }
265- if ( allowedOps . length === 0 ) {
266- return source
267- }
268- if ( ! isComponentsPathItemRef ( jsonPath ) ) {
269- return source
270- }
271259 const httpMethods = new Set < string > ( Object . values ( OpenAPIV3 . HttpMethods ) as string [ ] )
272- const copy : Record < string , unknown > = { ...( source as Record < string , unknown > ) }
273- Object . keys ( copy ) . forEach ( ( key : string ) => {
274- if ( httpMethods . has ( key ) && ! allowedOps . includes ( key ) ) {
275- delete copy [ key ]
260+ const filteredSource : Record < string , unknown > = { ...( source as Record < string , unknown > ) }
261+
262+ for ( const key of Object . keys ( filteredSource ) ) {
263+ if ( httpMethods . has ( key ) && ! allowedMethods . includes ( key ) ) {
264+ delete filteredSource [ key ]
276265 }
277- } )
278- return copy
266+ }
267+
268+ return filteredSource
279269}
280270
281- /**
282- * Extracts the set of HTTP methods allowed by the partial component found at jsonPath
283- * within the resultSpec. If the path does not resolve to an object, returns an empty list.
284- */
285271export const getAllowedHttpOps = ( resultSpec : unknown , jsonPath : JsonPath ) : string [ ] => {
286272 const resultComponents = getKeyValue ( resultSpec , ...jsonPath ) as unknown
287273 if ( typeof resultComponents !== 'object' || resultComponents === null ) {
@@ -310,41 +296,50 @@ const createSingleOperationSpec = (
310296 security ?: OpenAPIV3 . SecurityRequirementObject [ ] ,
311297 securitySchemes ?: { [ p : string ] : OpenAPIV3 . ReferenceObject | OpenAPIV3 . SecuritySchemeObject } ,
312298) : TYPE . RestOperationData => {
313- const pathData = document . paths [ path ] as OpenAPIV3 . PathItemObject
299+ const pathData = document . paths [ path ] as OpenAPIV3 . PathItemObject | undefined
300+ if ( ! pathData ) {
301+ throw new Error ( `Path "${ path } " not found in the document` )
302+ }
303+
304+ const baseSpec = {
305+ openapi : openapi ?? '3.0.0' ,
306+ ...takeIfDefined ( { servers } ) ,
307+ ...takeIfDefined ( { security } ) , // TODO: remove duplicates in security
308+ components : {
309+ ...takeIfDefined ( { securitySchemes } ) ,
310+ } ,
311+ }
312+
313+ if ( pathData . $ref ) {
314+ const cleanedDocument = resolveRefAndMap (
315+ document ,
316+ pathData . $ref ,
317+ ( pathItemObject : OpenAPIV3 . PathItemObject ) => ( {
318+ ...extractCommonPathItemProperties ( pathItemObject ) ,
319+ [ method ] : { ...pathItemObject [ method ] } ,
320+ } ) ,
321+ )
314322
315- const ref = pathData ?. $ref
316- if ( pathData && ref ) {
317- const cleanedDocument = resolveRefAndMap ( document , ref , ( pathItemObject : OpenAPIV3 . PathItemObject ) => ( {
318- ...extractCommonPathItemProperties ( pathItemObject ) ,
319- [ method ] : { ...pathItemObject [ method ] } ,
320- } ) )
321323 return {
322- openapi : openapi ?? '3.0.0' ,
323- ...takeIfDefined ( { servers } ) ,
324- ...takeIfDefined ( { security } ) , // TODO: remove duplicates in security
324+ ...baseSpec ,
325325 paths : {
326326 [ path ] : pathData ,
327327 } ,
328328 components : {
329- ...takeIfDefined ( { securitySchemes } ) ,
329+ ...baseSpec . components ,
330330 ...cleanedDocument ?. components ?? { } ,
331331 } ,
332332 }
333333 }
334334
335335 return {
336- openapi : openapi ?? '3.0.0' ,
337- ...takeIfDefined ( { servers } ) ,
338- ...takeIfDefined ( { security } ) , // TODO: remove duplicates in security
336+ ...baseSpec ,
339337 paths : {
340338 [ path ] : {
341339 ...extractCommonPathItemProperties ( pathData ) ,
342340 [ method ] : { ...pathData [ method ] } ,
343341 } ,
344342 } ,
345- components : {
346- ...takeIfDefined ( { securitySchemes } ) ,
347- } ,
348343 }
349344}
350345
0 commit comments