1- import type { MapKeysResult , MappingResolver } from '../types'
2- import { getStringValue , objectKeys , onlyExistedArrayIndexes } from '../utils'
1+ import { MapKeysResult , MappingResolver , NodeContext } from '../types'
2+ import {
3+ difference ,
4+ getStringValue ,
5+ intersection ,
6+ objectKeys ,
7+ onlyExistedArrayIndexes ,
8+ removeSlashes ,
9+ } from '../utils'
310import { mapPathParams } from './openapi3.utils'
11+ import { OpenAPIV3 } from 'openapi-types'
412
513export const singleOperationPathMappingResolver : MappingResolver < string > = ( before , after ) => {
614
@@ -23,32 +31,40 @@ export const singleOperationPathMappingResolver: MappingResolver<string> = (befo
2331 return result
2432}
2533
26- export const pathMappingResolver : MappingResolver < string > = ( before , after ) => {
34+ export const pathMappingResolver : MappingResolver < string > = ( before , after , ctx ) => {
2735
2836 const result : MapKeysResult < string > = { added : [ ] , removed : [ ] , mapped : { } }
2937
30- const originalBeforeKeys = objectKeys ( before )
31- const originalAfterKeys = objectKeys ( after )
32- const unifiedAfterKeys = originalAfterKeys . map ( hidePathParamNames )
38+ const unifyBeforePath = createPathUnifier ( ctx . before )
39+ const unifyAfterPath = createPathUnifier ( ctx . after )
3340
34- const notMappedAfterIndices = new Set ( originalAfterKeys . keys ( ) )
41+ const unifiedBeforeKeyToKey = Object . fromEntries ( objectKeys ( before ) . map ( key => [ unifyBeforePath ( key ) , key ] ) )
42+ const unifiedAfterKeyToKey = Object . fromEntries ( objectKeys ( after ) . map ( key => [ unifyAfterPath ( key ) , key ] ) )
3543
36- originalBeforeKeys . forEach ( beforeKey => {
37- const unifiedBeforePath = hidePathParamNames ( beforeKey )
38- const index = unifiedAfterKeys . indexOf ( unifiedBeforePath )
44+ const unifiedBeforeKeys = Object . keys ( unifiedBeforeKeyToKey )
45+ const unifiedAfterKeys = Object . keys ( unifiedAfterKeyToKey )
3946
40- if ( index < 0 ) {
41- // removed item
42- result . removed . push ( beforeKey )
43- } else {
44- // mapped items
45- result . mapped [ beforeKey ] = originalAfterKeys [ index ]
46- notMappedAfterIndices . delete ( index )
47- }
48- } )
47+ result . added = difference ( unifiedAfterKeys , unifiedBeforeKeys ) . map ( key => unifiedAfterKeyToKey [ key ] )
48+ result . removed = difference ( unifiedBeforeKeys , unifiedAfterKeys ) . map ( key => unifiedBeforeKeyToKey [ key ] )
49+ result . mapped = Object . fromEntries (
50+ intersection ( unifiedBeforeKeys , unifiedAfterKeys ) . map ( key => [ unifiedBeforeKeyToKey [ key ] , unifiedAfterKeyToKey [ key ] ] ) ,
51+ )
52+
53+ return result
54+ }
55+
56+ export const methodMappingResolver : MappingResolver < string > = ( before , after ) => {
57+
58+ const result : MapKeysResult < string > = { added : [ ] , removed : [ ] , mapped : { } }
59+
60+ const beforeKeys = objectKeys ( before )
61+ const afterKeys = objectKeys ( after )
4962
50- // added items
51- notMappedAfterIndices . forEach ( ( notMappedIndex ) => result . added . push ( originalAfterKeys [ notMappedIndex ] ) )
63+ result . added = difference ( afterKeys , beforeKeys )
64+ result . removed = difference ( beforeKeys , afterKeys )
65+
66+ const mapped = intersection ( beforeKeys , afterKeys )
67+ mapped . forEach ( key => result . mapped [ key ] = key )
5268
5369 return result
5470}
@@ -175,6 +191,31 @@ function isWildcardCompatible(beforeType: string, afterType: string): boolean {
175191 return true
176192}
177193
194+ // todo copy-paste from api-processor
195+ export const extractOperationBasePath = ( servers ?: OpenAPIV3 . ServerObject [ ] ) : string => {
196+ if ( ! Array . isArray ( servers ) || ! servers . length ) { return '' }
197+
198+ try {
199+ const [ firstServer ] = servers
200+ let serverUrl = firstServer . url
201+ const { variables = { } } = firstServer
202+
203+ for ( const param of Object . keys ( variables ) ) {
204+ serverUrl = serverUrl . replace ( new RegExp ( `{${ param } }` , 'g' ) , variables [ param ] . default )
205+ }
206+
207+ const { pathname } = new URL ( serverUrl , 'https://localhost' )
208+ return pathname . slice ( - 1 ) === '/' ? pathname . slice ( 0 , - 1 ) : pathname
209+ } catch ( error ) {
210+ return ''
211+ }
212+ }
213+
214+ export function createPathUnifier ( nodeContext : NodeContext ) : ( path : string ) => string {
215+ const serverPrefix = extractOperationBasePath ( ( nodeContext . root as OpenAPIV3 . Document ) . servers ) // /api/v2
216+ return ( path ) => removeSlashes ( `${ serverPrefix } ${ hidePathParamNames ( path ) } ` )
217+ }
218+
178219export function hidePathParamNames ( path : string ) : string {
179220 return path . replace ( PATH_PARAMETER_REGEXP , PATH_PARAM_UNIFIED_PLACEHOLDER )
180221}
0 commit comments