[object Object]

← back to Cli Printing Press

fix(cli): normalize routing prefixes in OpenAPI paths (#296)

1e06456c34869c2e356cd7c10ca7c601d829c93d · 2026-04-25 18:15:19 -0700 · Trevin Chow

Files touched

Diff

commit 1e06456c34869c2e356cd7c10ca7c601d829c93d
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Sat Apr 25 18:15:19 2026 -0700

    fix(cli): normalize routing prefixes in OpenAPI paths (#296)
---
 internal/openapi/parser.go      | 27 +++++++++++++++++----------
 internal/openapi/parser_test.go |  2 ++
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/internal/openapi/parser.go b/internal/openapi/parser.go
index b7086f52..b2c1cb78 100644
--- a/internal/openapi/parser.go
+++ b/internal/openapi/parser.go
@@ -2302,17 +2302,24 @@ func pathSegmentsAfterBase(path, basePath string) []string {
 		}
 	}
 
-	if len(segments) > 0 && isVersionSegment(segments[0]) {
-		segments = segments[1:]
-	}
+	for len(segments) > 0 {
+		if isVersionSegment(segments[0]) {
+			segments = segments[1:]
+			continue
+		}
 
-	// Strip generic API routing prefixes when followed by a concrete resource
-	// segment. "api", "apis", "rest" as the first segment after version are
-	// routing infrastructure, not resource names. /v1/api/users → "users".
-	// Do NOT strip when the next segment is a path param ({id}), because
-	// /api/{id} means "api" IS the resource.
-	if len(segments) >= 2 && isGenericAPIPrefix(segments[0]) && !isPathParamSegment(segments[1]) {
-		segments = segments[1:]
+		// Strip generic API routing prefixes when followed by a concrete
+		// resource or another routing segment. "api", "apis", "rest" are
+		// infrastructure, not resource names. /v1/api/users and
+		// /api/v2/users both normalize to "users".
+		// Do NOT strip when the next segment is a path param ({id}), because
+		// /api/{id} means "api" IS the resource.
+		if len(segments) >= 2 && isGenericAPIPrefix(segments[0]) && !isPathParamSegment(segments[1]) {
+			segments = segments[1:]
+			continue
+		}
+
+		break
 	}
 
 	return segments
diff --git a/internal/openapi/parser_test.go b/internal/openapi/parser_test.go
index e8517771..b43e208e 100644
--- a/internal/openapi/parser_test.go
+++ b/internal/openapi/parser_test.go
@@ -252,6 +252,8 @@ func TestPathSegmentsStripsGenericAPIPrefix(t *testing.T) {
 		{"keeps api when followed by path param", "/api/{id}", "", "api"},
 		{"keeps rest when followed by path param", "/rest/{job_id}/runs", "", "rest"},
 		{"strips version then api", "/v1/api/networkentity", "", "networkentity"},
+		{"strips api then version", "/api/v2/pokemon", "", "pokemon"},
+		{"strips version then api then version", "/v2/api/v1/pokemon", "", "pokemon"},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {

← 19e996ae chore(main): release 2.3.5 (#295)  ·  back to Cli Printing Press  ·  chore(main): release 2.3.6 (#297) 42b0f1f4 →