Skip to content

Commit eece2ef

Browse files
PATCH: Fix pagination detection with resolution in OpenAPI processing
1 parent d5f2ab0 commit eece2ef

File tree

2 files changed

+457
-19
lines changed

2 files changed

+457
-19
lines changed

internal/pagination/pagination.go

Lines changed: 93 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,27 @@ type ProcessResult struct {
6060

6161
// DetectPaginationInParams detects pagination strategies in operation parameters
6262
func DetectPaginationInParams(params *yaml.Node) []DetectedPagination {
63+
return DetectPaginationInParamsWithDoc(params, nil)
64+
}
65+
66+
// DetectPaginationInParamsWithDoc detects pagination strategies in operation parameters with document context for $ref resolution
67+
func DetectPaginationInParamsWithDoc(params *yaml.Node, doc *yaml.Node) []DetectedPagination {
6368
var detected []DetectedPagination
6469

6570
if params == nil || params.Kind != yaml.SequenceNode {
6671
return detected
6772
}
6873

74+
strategyParams := collectStrategyParams(params, doc)
75+
76+
// Convert to DetectedPagination, filtering out weak strategies
77+
detected = filterWeakStrategies(strategyParams)
78+
79+
return detected
80+
}
81+
82+
// collectStrategyParams scans through parameters and collects which strategies each parameter belongs to
83+
func collectStrategyParams(params *yaml.Node, doc *yaml.Node) map[string][]string {
6984
strategyParams := make(map[string][]string)
7085

7186
// Scan through parameters
@@ -74,7 +89,7 @@ func DetectPaginationInParams(params *yaml.Node) []DetectedPagination {
7489
continue
7590
}
7691

77-
paramName := getStringValue(param, "name")
92+
paramName := extractParameterName(param, doc)
7893
if paramName == "" {
7994
continue
8095
}
@@ -89,23 +104,36 @@ func DetectPaginationInParams(params *yaml.Node) []DetectedPagination {
89104
}
90105
}
91106

92-
// Convert to DetectedPagination, filtering out weak strategies
107+
return strategyParams
108+
}
109+
110+
// extractParameterName extracts the parameter name from a param node, handling $ref resolution
111+
func extractParameterName(param *yaml.Node, doc *yaml.Node) string {
112+
var paramName string
113+
114+
// Handle $ref by resolving it first
115+
if ref := getNodeValue(param, "$ref"); ref != nil && doc != nil {
116+
refPath := ref.Value
117+
resolvedParam := resolveRef(refPath, doc)
118+
if resolvedParam != nil {
119+
paramName = getStringValue(resolvedParam, "name")
120+
}
121+
} else {
122+
paramName = getStringValue(param, "name")
123+
}
124+
125+
return paramName
126+
}
127+
128+
// filterWeakStrategies converts strategy params to DetectedPagination, filtering out weak strategies
129+
func filterWeakStrategies(strategyParams map[string][]string) []DetectedPagination {
130+
var detected []DetectedPagination
131+
93132
// A strategy is considered "weak" if it only has shared parameters
94133
sharedParams := findSharedParams()
95134

96135
for strategy, params := range strategyParams {
97-
// Check if this strategy has any non-shared parameters
98-
hasNonSharedParams := false
99-
for _, param := range params {
100-
if !sharedParams[param] {
101-
hasNonSharedParams = true
102-
break
103-
}
104-
}
105-
106-
// Only include strategies that have non-shared parameters
107-
// This prevents strategies like "offset" with only "include_totals" from interfering
108-
if hasNonSharedParams {
136+
if hasNonSharedParams(params, sharedParams) {
109137
detected = append(detected, DetectedPagination{
110138
Strategy: strategy,
111139
Parameters: params,
@@ -116,6 +144,16 @@ func DetectPaginationInParams(params *yaml.Node) []DetectedPagination {
116144
return detected
117145
}
118146

147+
// hasNonSharedParams checks if a strategy has any non-shared parameters
148+
func hasNonSharedParams(params []string, sharedParams map[string]bool) bool {
149+
for _, param := range params {
150+
if !sharedParams[param] {
151+
return true
152+
}
153+
}
154+
return false
155+
}
156+
119157
// findSharedParams identifies parameters that belong to multiple strategies
120158
func findSharedParams() map[string]bool {
121159
sharedParams := make(map[string]bool)
@@ -255,7 +293,7 @@ func ProcessEndpointWithDoc(operation *yaml.Node, doc *yaml.Node, opts Options)
255293

256294
// detectPaginationStrategies extracts pagination strategies from params and responses
257295
func detectPaginationStrategies(params, responses *yaml.Node, doc *yaml.Node) *paginationStrategies {
258-
paramPagination := DetectPaginationInParams(params)
296+
paramPagination := DetectPaginationInParamsWithDoc(params, doc)
259297
responsePagination := DetectPaginationInResponsesWithDoc(responses, doc)
260298

261299
paramStrategies := make(map[string]bool)
@@ -294,7 +332,7 @@ func needsProcessingCheck(strategies *paginationStrategies, params, responses *y
294332
return true
295333
}
296334

297-
if hasOrphanedSharedParams(params, strategies.paramStrategies) {
335+
if hasOrphanedSharedParamsWithDoc(params, strategies.paramStrategies, doc) {
298336
return true
299337
}
300338

@@ -317,6 +355,11 @@ func hasResponseCleanupNeeded(strategies *paginationStrategies) bool {
317355

318356
// hasOrphanedSharedParams checks for orphaned shared parameters
319357
func hasOrphanedSharedParams(params *yaml.Node, paramStrategies map[string]bool) bool {
358+
return hasOrphanedSharedParamsWithDoc(params, paramStrategies, nil)
359+
}
360+
361+
// hasOrphanedSharedParamsWithDoc checks for orphaned shared parameters with document context for $ref resolution
362+
func hasOrphanedSharedParamsWithDoc(params *yaml.Node, paramStrategies map[string]bool, doc *yaml.Node) bool {
320363
if params == nil {
321364
return false
322365
}
@@ -327,7 +370,20 @@ func hasOrphanedSharedParams(params *yaml.Node, paramStrategies map[string]bool)
327370
continue
328371
}
329372

330-
paramName := getStringValue(param, "name")
373+
var paramName string
374+
var resolvedParam *yaml.Node
375+
376+
// Handle $ref by resolving it first
377+
if ref := getNodeValue(param, "$ref"); ref != nil && doc != nil {
378+
refPath := ref.Value
379+
resolvedParam = resolveRef(refPath, doc)
380+
if resolvedParam != nil {
381+
paramName = getStringValue(resolvedParam, "name")
382+
}
383+
} else {
384+
paramName = getStringValue(param, "name")
385+
}
386+
331387
if paramName == "" || !sharedParams[paramName] {
332388
continue
333389
}
@@ -384,7 +440,7 @@ func selectBestStrategy(strategies *paginationStrategies, opts Options) string {
384440
// processEndpointCleanup performs the actual cleanup of params and responses
385441
func processEndpointCleanup(params, responses *yaml.Node, selectedStrategy string, allPagination []DetectedPagination, doc *yaml.Node, result *ProcessResult) (*ProcessResult, error) {
386442
if params != nil {
387-
removed := removeUnwantedParams(params, selectedStrategy, allPagination)
443+
removed := removeUnwantedParamsWithDoc(params, selectedStrategy, allPagination, doc)
388444
result.RemovedParams = removed
389445
if len(removed) > 0 {
390446
result.Changed = true
@@ -405,6 +461,11 @@ func processEndpointCleanup(params, responses *yaml.Node, selectedStrategy strin
405461

406462
// removeUnwantedParams removes parameters that don't match the selected strategy
407463
func removeUnwantedParams(params *yaml.Node, selectedStrategy string, detected []DetectedPagination) []string {
464+
return removeUnwantedParamsWithDoc(params, selectedStrategy, detected, nil)
465+
}
466+
467+
// removeUnwantedParamsWithDoc removes parameters that don't match the selected strategy with document context for $ref resolution
468+
func removeUnwantedParamsWithDoc(params *yaml.Node, selectedStrategy string, detected []DetectedPagination, doc *yaml.Node) []string {
408469
var removed []string
409470

410471
if params.Kind != yaml.SequenceNode {
@@ -420,7 +481,20 @@ func removeUnwantedParams(params *yaml.Node, selectedStrategy string, detected [
420481
continue
421482
}
422483

423-
paramName := getStringValue(param, "name")
484+
var paramName string
485+
var resolvedParam *yaml.Node
486+
487+
// Handle $ref by resolving it first
488+
if ref := getNodeValue(param, "$ref"); ref != nil && doc != nil {
489+
refPath := ref.Value
490+
resolvedParam = resolveRef(refPath, doc)
491+
if resolvedParam != nil {
492+
paramName = getStringValue(resolvedParam, "name")
493+
}
494+
} else {
495+
paramName = getStringValue(param, "name")
496+
}
497+
424498
if paramName == "" {
425499
newContent = append(newContent, param)
426500
continue

0 commit comments

Comments
 (0)