Skip to content

Commit 981e057

Browse files
bugerclaude
andcommitted
fix: add missing sort import, PathLess func, and sortURLSpecsByPathPriority for backport
The cherry-pick from master was missing: - "sort" import in gateway/api_definition.go - oasutil.PathLess function used by sortURLSpecsByPathPriority - sortURLSpecsByPathPriority function referenced by tests - Exported PathParamRegex in oasutil for cross-package access Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent b4ef7e8 commit 981e057

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

gateway/api_definition.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"net/url"
1313
"os"
1414
"path/filepath"
15+
"sort"
1516
"strings"
1617
"sync/atomic"
1718
texttemplate "text/template"
@@ -23,6 +24,7 @@ import (
2324
"github.com/TykTechnologies/tyk/internal/httpctx"
2425
"github.com/TykTechnologies/tyk/internal/httputil"
2526
"github.com/TykTechnologies/tyk/internal/mcp"
27+
oasutil "github.com/TykTechnologies/tyk/internal/oasutil"
2628

2729
"github.com/getkin/kin-openapi/routers/gorillamux"
2830

@@ -1631,6 +1633,14 @@ func pathParamPatternLength(oasPath, method string, oasPaths *openapi3.Paths) in
16311633
return total
16321634
}
16331635

1636+
// sortURLSpecsByPathPriority sorts URLSpec entries using the same path priority
1637+
// rules as oasutil.SortByPathLength, ensuring consistent ordering across the gateway.
1638+
func sortURLSpecsByPathPriority(specs []URLSpec) {
1639+
sort.Slice(specs, func(i, j int) bool {
1640+
return oasutil.PathLess(specs[i].OASPath, specs[j].OASPath)
1641+
})
1642+
}
1643+
16341644
// compileOASMockResponsePathSpec extracts MockResponse operations from OAS middleware
16351645
// and converts them to URLSpec entries that use the standard regex-based path matching algorithm.
16361646
// This ensures OAS mockResponse middleware respects gateway configurations like

internal/oasutil/paths.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ type PathItem struct {
1717
Path string
1818
}
1919

20-
var pathParamRegex = regexp.MustCompile(`\{[^}]+\}`)
20+
// PathParamRegex matches path parameters like {id} or {employeeId} in OAS paths.
21+
var PathParamRegex = regexp.MustCompile(`\{[^}]+\}`)
22+
23+
// Keep the unexported alias for backward compatibility within this package.
24+
var pathParamRegex = PathParamRegex
2125

2226
// ExtractPaths will extract paths with the given order.
2327
func ExtractPaths(in openapi3.Paths, order []string) []PathItem {
@@ -35,6 +39,37 @@ func ExtractPaths(in openapi3.Paths, order []string) []PathItem {
3539
return result
3640
}
3741

42+
// PathLess reports whether pathA should sort before pathB using the
43+
// standard Tyk path priority rules:
44+
// - Strip path parameters {…} before comparing
45+
// - If two paths are equal after stripping, the non-parameterised one goes first
46+
// - Sort by number of "/" segments (more segments first)
47+
// - Sort by string length (longer first)
48+
// - Alphabetical for equal length
49+
func PathLess(pathA, pathB string) bool {
50+
a := PathParamRegex.ReplaceAllString(pathA, "")
51+
b := PathParamRegex.ReplaceAllString(pathB, "")
52+
53+
// handle /sub and /sub{id} order with raw path.
54+
if a == b {
55+
// we're reversing paths here so path with
56+
// parameter is sorted after the literal.
57+
a, b = pathB, pathA
58+
}
59+
60+
// sort by number of path fragments
61+
ka, kb := strings.Count(a, "/"), strings.Count(b, "/")
62+
if ka != kb {
63+
return ka > kb
64+
}
65+
66+
la, lb := len(a), len(b)
67+
if la == lb {
68+
return a < b
69+
}
70+
return la > lb
71+
}
72+
3873
// SortByPathLength decomposes an openapi3.Paths to a sorted []PathItem.
3974
// The sorting takes the length of the paths into account, as well as
4075
// path parameters, sorting them by length descending, and ordering

0 commit comments

Comments
 (0)