From 8e83f44c577cccca5409d49c07dc6cc461b08d85 Mon Sep 17 00:00:00 2001 From: Michael Heap Date: Mon, 9 Feb 2026 17:00:10 +0000 Subject: [PATCH 1/5] Only generate header routes when required: true There is a `TreatAllHeadersAsRequired` option to keep the old behaviour if needed. I do not intend to add a flag to deck yet unless people ask for this functionality --- ...routes-with-optional-headers.expected.json | 82 +++++++++ .../27-routes-with-optional-headers.yaml | 34 ++++ ...-with-optional-headers-force.expected.json | 171 ++++++++++++++++++ ...28-routes-with-optional-headers-force.yaml | 36 ++++ openapi2kong/openapi2kong.go | 18 +- openapi2kong/openapi2kong_test.go | 47 +++-- 6 files changed, 368 insertions(+), 20 deletions(-) create mode 100644 openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json create mode 100644 openapi2kong/oas3_testfiles/27-routes-with-optional-headers.yaml create mode 100644 openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json create mode 100644 openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.yaml diff --git a/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json new file mode 100644 index 0000000..5acea3d --- /dev/null +++ b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json @@ -0,0 +1,82 @@ +{ + "_format_version": "3.0", + "services": [ + { + "host": "server1.com", + "id": "0907c4ab-d9e4-5d21-813b-c57a97eeaad9", + "name": "simple-api-overview", + "path": "/", + "plugins": [], + "port": 443, + "protocol": "https", + "routes": [ + { + "id": "4d47cf34-1c69-5228-acf1-b2c05994bd02", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-doc-service", + "paths": [ + "~/path1/(?\u003cpathparam\u003e[^#?/]+)$" + ], + "plugins": [], + "regex_priority": 100, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_27-routes-with-optional-headers.yaml" + ] + }, + { + "headers": { + "X-Path-Level-API-Version": [ + "v1" + ] + }, + "id": "7d086c23-ab4b-567d-969b-356acd50e716", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_0", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_27-routes-with-optional-headers.yaml" + ] + }, + { + "headers": { + "X-Path-Level-API-Version": [ + "v2" + ] + }, + "id": "433a9a6b-712c-5b47-8c70-64ce872aabea", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_1", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_27-routes-with-optional-headers.yaml" + ] + } + ], + "tags": [ + "OAS3_import", + "OAS3file_27-routes-with-optional-headers.yaml" + ] + } + ], + "upstreams": [] +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.yaml b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.yaml new file mode 100644 index 0000000..88747d0 --- /dev/null +++ b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.yaml @@ -0,0 +1,34 @@ +openapi: "3.0.0" +info: + title: Simple API overview + version: v2 +servers: + - url: https://server1.com/ +paths: + /path2: + parameters: + - in: header + name: X-Path-Level-API-Version + required: true + schema: + type: string + enum: + - v1 + - v2 + get: + parameters: + - in: header + name: X-Method-Level-Int-Header + required: false + schema: + type: integer + enum: + - 1 + - 2 + - 3 + operationId: get-path2-service + summary: List API versions Path2 + responses: + "200": + description: |- + 200 response diff --git a/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json b/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json new file mode 100644 index 0000000..a9e031f --- /dev/null +++ b/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json @@ -0,0 +1,171 @@ +{ + "_format_version": "3.0", + "services": [ + { + "host": "server1.com", + "id": "0907c4ab-d9e4-5d21-813b-c57a97eeaad9", + "name": "simple-api-overview", + "path": "/", + "plugins": [], + "port": 443, + "protocol": "https", + "routes": [ + { + "headers": { + "X-Method-Level-Int-Header": [ + "1" + ], + "X-Path-Level-API-Version": [ + "v1" + ] + }, + "id": "7d086c23-ab4b-567d-969b-356acd50e716", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_0", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + }, + { + "headers": { + "X-Method-Level-Int-Header": [ + "1" + ], + "X-Path-Level-API-Version": [ + "v2" + ] + }, + "id": "433a9a6b-712c-5b47-8c70-64ce872aabea", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_1", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + }, + { + "headers": { + "X-Method-Level-Int-Header": [ + "2" + ], + "X-Path-Level-API-Version": [ + "v1" + ] + }, + "id": "f8027c37-0b74-5e33-aeba-173407cdc809", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_2", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + }, + { + "headers": { + "X-Method-Level-Int-Header": [ + "2" + ], + "X-Path-Level-API-Version": [ + "v2" + ] + }, + "id": "66217d2a-5a98-5803-b917-deec54e2c11e", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_3", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + }, + { + "headers": { + "X-Method-Level-Int-Header": [ + "3" + ], + "X-Path-Level-API-Version": [ + "v1" + ] + }, + "id": "df2fd6f0-3aaa-5888-80fa-30a218fe26a5", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_4", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + }, + { + "headers": { + "X-Method-Level-Int-Header": [ + "3" + ], + "X-Path-Level-API-Version": [ + "v2" + ] + }, + "id": "168aa36e-da1e-5b6f-9c8e-5242ee9516a5", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service_5", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + } + ], + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] + } + ], + "upstreams": [] +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.yaml b/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.yaml new file mode 100644 index 0000000..ea3b905 --- /dev/null +++ b/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.yaml @@ -0,0 +1,36 @@ +x-test-config: + treatAllHeadersAsRequired: true +openapi: "3.0.0" +info: + title: Simple API overview + version: v2 +servers: + - url: https://server1.com/ +paths: + /path2: + parameters: + - in: header + name: X-Path-Level-API-Version + required: true + schema: + type: string + enum: + - v1 + - v2 + get: + parameters: + - in: header + name: X-Method-Level-Int-Header + required: false + schema: + type: integer + enum: + - 1 + - 2 + - 3 + operationId: get-path2-service + summary: List API versions Path2 + responses: + "200": + description: |- + 200 response diff --git a/openapi2kong/openapi2kong.go b/openapi2kong/openapi2kong.go index aa7619c..c10f855 100644 --- a/openapi2kong/openapi2kong.go +++ b/openapi2kong/openapi2kong.go @@ -48,6 +48,8 @@ type O2kOptions struct { IgnoreSecurityErrors bool // Ignore circular references IgnoreCircularRefs bool + // Generate separate routes for each header enum even if required: false + TreatAllHeadersAsRequired bool } // setDefaults sets the defaults for the OpenAPI2Kong operation. @@ -559,13 +561,13 @@ func findParameterSchema( func findHeaderParamsForRouting( operationLevelParameters []*v3.Parameter, pathLevelParameters []*v3.Parameter, + treatAllHeadersAsRequired bool, ) []*v3.Parameter { headerParamProcessed := make(map[string]bool) var result []*v3.Parameter // Store in array so output is deterministic - iterating over map is not. for _, param := range operationLevelParameters { - if param.In == "header" && param.Schema != nil && - param.Schema.Schema() != nil && len(param.Schema.Schema().Enum) > 0 { + if shouldAddHeaderParameter(param, treatAllHeadersAsRequired) { headerParamProcessed[param.Name] = true result = append(result, param) } @@ -573,8 +575,7 @@ func findHeaderParamsForRouting( for _, param := range pathLevelParameters { // Operation level params override path level params, so ignore if already present. - if param.In == "header" && param.Schema != nil && param.Schema.Schema() != nil && - len(param.Schema.Schema().Enum) > 0 && !headerParamProcessed[param.Name] { + if shouldAddHeaderParameter(param, treatAllHeadersAsRequired) && !headerParamProcessed[param.Name] { headerParamProcessed[param.Name] = true result = append(result, param) } @@ -583,6 +584,12 @@ func findHeaderParamsForRouting( return result } +func shouldAddHeaderParameter(param *v3.Parameter, treatAllHeadersAsRequired bool) bool { + hasEnum := param.Schema != nil && param.Schema.Schema() != nil && len(param.Schema.Schema().Enum) > 0 + isRequired := *param.Required || treatAllHeadersAsRequired + return param.In == "header" && hasEnum && isRequired +} + // Based on given headers and their possible values, create all possible combinations func constructHeaderCombinationsForRouting(headers []*v3.Parameter) []map[string]any { headerValues := make([][]any, len(headers)) @@ -1222,7 +1229,8 @@ func Convert(content []byte, opts O2kOptions) (map[string]interface{}, error) { route["strip_path"] = false // Default to false since we do not want to strip full-regex paths by default } - headerParams := findHeaderParamsForRouting(operation.Parameters, pathitem.Parameters) + headerParams := findHeaderParamsForRouting(operation.Parameters, pathitem.Parameters, opts.TreatAllHeadersAsRequired) + if len(headerParams) > 0 { // This operation has header parameters, we need to create different routes based on the header values headerValueCombinations := constructHeaderCombinationsForRouting(headerParams) diff --git a/openapi2kong/openapi2kong_test.go b/openapi2kong/openapi2kong_test.go index 5dee0a2..f4227ba 100644 --- a/openapi2kong/openapi2kong_test.go +++ b/openapi2kong/openapi2kong_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "go.yaml.in/yaml/v4" ) const fixturePath = "./oas3_testfiles/" @@ -77,22 +78,38 @@ func Test_Openapi2kong(t *testing.T) { for _, file := range files { fileNameIn := file.Name() - fileNameExpected := strings.TrimSuffix(fileNameIn, ".yaml") + ".expected.json" - fileNameOut := strings.TrimSuffix(fileNameIn, ".yaml") + ".generated.json" - dataIn, _ := os.ReadFile(fixturePath + fileNameIn) - dataOut, err := Convert(dataIn, O2kOptions{ - Tags: []string{"OAS3_import", "OAS3file_" + fileNameIn}, - OIDC: true, + fileNameBase := strings.TrimSuffix(fileNameIn, ".yaml") + t.Run(fileNameBase, func(t *testing.T) { + fileNameExpected := fileNameBase + ".expected.json" + fileNameOut := fileNameBase + ".generated.json" + dataIn, _ := os.ReadFile(fixturePath + fileNameIn) + + // Test option configuration + allHeadersRequired := false + + var config map[string]any + yaml.Unmarshal(dataIn, &config) + if testConfig, ok := config["x-test-config"].(map[string]any); ok { + if val, ok := testConfig["treatAllHeadersAsRequired"]; ok { + allHeadersRequired = val.(bool) + } + } + + dataOut, err := Convert(dataIn, O2kOptions{ + Tags: []string{"OAS3_import", "OAS3file_" + fileNameIn}, + OIDC: true, + TreatAllHeadersAsRequired: allHeadersRequired, + }) + if err != nil { + t.Error(fmt.Sprintf("'%s' didn't expect error: %%w", fixturePath+fileNameIn), err) + } else { + JSONOut, _ := json.MarshalIndent(dataOut, "", " ") + os.WriteFile(fixturePath+fileNameOut, JSONOut, 0o600) + JSONExpected, _ := os.ReadFile(fixturePath + fileNameExpected) + assert.JSONEq(t, string(JSONExpected), string(JSONOut), + "'%s': the JSON blobs should be equal", fixturePath+fileNameIn) + } }) - if err != nil { - t.Error(fmt.Sprintf("'%s' didn't expect error: %%w", fixturePath+fileNameIn), err) - } else { - JSONOut, _ := json.MarshalIndent(dataOut, "", " ") - os.WriteFile(fixturePath+fileNameOut, JSONOut, 0o600) - JSONExpected, _ := os.ReadFile(fixturePath + fileNameExpected) - assert.JSONEq(t, string(JSONExpected), string(JSONOut), - "'%s': the JSON blobs should be equal", fixturePath+fileNameIn) - } } } From aa708cfd144e030b9c9564325d9a41d067e95fd9 Mon Sep 17 00:00:00 2001 From: Michael Heap Date: Mon, 9 Feb 2026 18:00:08 +0000 Subject: [PATCH 2/5] Create unique plugin IDs when using header routes --- ...-headers-duplicate-plugin-id.expected.json | 95 +++++++++++++++++++ ...utes-with-headers-duplicate-plugin-id.yaml | 21 ++++ openapi2kong/openapi2kong.go | 22 +++++ 3 files changed, 138 insertions(+) create mode 100644 openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json create mode 100644 openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.yaml diff --git a/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json b/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json new file mode 100644 index 0000000..c9e2f8a --- /dev/null +++ b/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json @@ -0,0 +1,95 @@ +{ + "_format_version": "3.0", + "services": [ + { + "host": "localhost", + "id": "49bfd8df-3368-534b-b7d6-4a4c4e4e1252", + "name": "example-api", + "path": "/", + "plugins": [], + "port": 443, + "protocol": "https", + "routes": [ + { + "headers": { + "Content-Type": [ + "application/xml" + ] + }, + "id": "a079e253-1adf-5c97-9759-bf8d74731856", + "methods": [ + "POST" + ], + "name": "example-api_exampleoperation_0", + "paths": [ + "~/api/v1/example$" + ], + "plugins": [ + { + "config": { + "limit": [ + 500 + ] + }, + "enabled": true, + "id": "bddcead7-86ba-5276-ad3b-04847c81ccee", + "name": "rate-limiting-advanced", + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] + }, + { + "headers": { + "Content-Type": [ + "text/xml" + ] + }, + "id": "bbbcee70-d94a-5f9d-8df1-bfb895589dcb", + "methods": [ + "POST" + ], + "name": "example-api_exampleoperation_1", + "paths": [ + "~/api/v1/example$" + ], + "plugins": [ + { + "config": { + "limit": [ + 500 + ] + }, + "enabled": true, + "id": "339689c4-0aab-591e-bd21-88d949cfc124", + "name": "rate-limiting-advanced", + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] + } + ], + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] + } + ], + "upstreams": [] +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.yaml b/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.yaml new file mode 100644 index 0000000..a87bbdf --- /dev/null +++ b/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.3 +info: + version: v1 + title: Example API +paths: + /api/v1/example: + post: + operationId: exampleOperation + x-kong-plugin-rate-limiting-advanced: + enabled: true + config: + limit: [500] + parameters: + - name: Content-Type + in: header + required: true + schema: + type: string + enum: + - application/xml + - text/xml diff --git a/openapi2kong/openapi2kong.go b/openapi2kong/openapi2kong.go index c10f855..3d83ee8 100644 --- a/openapi2kong/openapi2kong.go +++ b/openapi2kong/openapi2kong.go @@ -1242,6 +1242,28 @@ func Convert(content []byte, opts O2kOptions) (map[string]interface{}, error) { clonedRoute["name"] = fmt.Sprintf("%s_%v", operationBaseName, i) if !opts.SkipID { clonedRoute["id"] = uuid.NewSHA1(opts.UUIDNamespace, []byte(clonedRoute["name"].(string))).String() + + // Update the route plugin IDs to take in to account the header + // or we get duplicate plugin IDs + if clonedRoute["plugins"] != nil { + combinationJSON, err := json.Marshal(combination) + if err != nil { + return nil, fmt.Errorf("failed to marshal header combination for plugin ID generation: %w", err) + } + + headerBase := string(combinationJSON) + + plugins, ok := clonedRoute["plugins"].([]any) + if ok { + for k, v := range plugins { + p, ok := v.(map[string]interface{}) + if ok { + p["id"] = createPluginID(opts.UUIDNamespace, operationBaseName+headerBase, p) + } + plugins[k] = p + } + } + } } newRoutes = append(newRoutes, clonedRoute) } From 64631723753938c56dae7cd98673e117640d8dea Mon Sep 17 00:00:00 2001 From: Harsha Dixit Date: Sat, 7 Mar 2026 19:12:16 +0530 Subject: [PATCH 3/5] tests: fix IDs in older tests for plugin gen in openapi2kong --- ...ath-operation-parameter-schema.expected.json | 4 ++-- ...r-plugin-path-parameter-schema.expected.json | 4 ++-- ...7-routes-with-optional-headers.expected.json | 17 ----------------- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json index 36a5b95..dbf48c8 100644 --- a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json +++ b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json @@ -40,7 +40,7 @@ "version": "draft4" }, "enabled": true, - "id": "8384b338-2d12-5035-902c-a20fd9ddd6fb", + "id": "f5d69916-9f2e-546a-827a-40c55909a877", "name": "request-validator", "tags": [ "OAS3_import", @@ -85,7 +85,7 @@ "version": "draft4" }, "enabled": true, - "id": "eb7440df-3f27-5647-bab9-fec82845e2b9", + "id": "a54b7fcf-7938-5526-8235-1687533d5214", "name": "request-validator", "tags": [ "OAS3_import", diff --git a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json index d0a0452..33d72f8 100644 --- a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json +++ b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json @@ -40,7 +40,7 @@ "version": "draft4" }, "enabled": true, - "id": "ddb9f3cc-b11a-58f2-87e0-e47d8eca0242", + "id": "60c1ec68-951a-5aa6-999b-ab57a3960692", "name": "request-validator", "tags": [ "OAS3_import", @@ -85,7 +85,7 @@ "version": "draft4" }, "enabled": true, - "id": "1da08580-9237-584f-8213-9d64c1a87782", + "id": "26fd997b-a957-544c-a9e7-a394c4e04240", "name": "request-validator", "tags": [ "OAS3_import", diff --git a/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json index 5acea3d..ffa287b 100644 --- a/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json +++ b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json @@ -10,23 +10,6 @@ "port": 443, "protocol": "https", "routes": [ - { - "id": "4d47cf34-1c69-5228-acf1-b2c05994bd02", - "methods": [ - "GET" - ], - "name": "simple-api-overview_get-doc-service", - "paths": [ - "~/path1/(?\u003cpathparam\u003e[^#?/]+)$" - ], - "plugins": [], - "regex_priority": 100, - "strip_path": false, - "tags": [ - "OAS3_import", - "OAS3file_27-routes-with-optional-headers.yaml" - ] - }, { "headers": { "X-Path-Level-API-Version": [ From 8af1d8ff471b1bbb6a8a05e91c9e33c0feb16f22 Mon Sep 17 00:00:00 2001 From: Harsha Dixit Date: Tue, 10 Mar 2026 18:28:30 +0530 Subject: [PATCH 4/5] fix: add fallback route when header enum also exists --- openapi2kong/openapi2kong.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openapi2kong/openapi2kong.go b/openapi2kong/openapi2kong.go index 3d83ee8..39bf01d 100644 --- a/openapi2kong/openapi2kong.go +++ b/openapi2kong/openapi2kong.go @@ -1268,9 +1268,11 @@ func Convert(content []byte, opts O2kOptions) (map[string]interface{}, error) { newRoutes = append(newRoutes, clonedRoute) } operationRoutes = append(operationRoutes, newRoutes...) - } else { - operationRoutes = append(operationRoutes, route) } + + // If no required header params exist, we add one route. + // In case they exist, this acts as a fallback route without header based routing. + operationRoutes = append(operationRoutes, route) operationService["routes"] = operationRoutes } } From a15fc979f570fce430d79a95d01ca9e1258dd29f Mon Sep 17 00:00:00 2001 From: Harsha Dixit Date: Tue, 10 Mar 2026 18:28:57 +0530 Subject: [PATCH 5/5] tests: fix tests for header based routing --- .../25-routes-with-headers.expected.json | 17 ++++ ...h-operation-parameter-schema.expected.json | 82 ++++++++++++++++++- ...plugin-path-parameter-schema.expected.json | 82 ++++++++++++++++++- ...routes-with-optional-headers.expected.json | 17 ++++ ...-with-optional-headers-force.expected.json | 17 ++++ ...-headers-duplicate-plugin-id.expected.json | 32 ++++++++ 6 files changed, 245 insertions(+), 2 deletions(-) diff --git a/openapi2kong/oas3_testfiles/25-routes-with-headers.expected.json b/openapi2kong/oas3_testfiles/25-routes-with-headers.expected.json index 70bb337..a9eae39 100644 --- a/openapi2kong/oas3_testfiles/25-routes-with-headers.expected.json +++ b/openapi2kong/oas3_testfiles/25-routes-with-headers.expected.json @@ -176,6 +176,23 @@ "OAS3_import", "OAS3file_25-routes-with-headers.yaml" ] + }, + { + "id": "27ea9a0a-216e-5c92-ae0b-8c9aa8493750", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_25-routes-with-headers.yaml" + ] } ], "tags": [ diff --git a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json index dbf48c8..a8f0851 100644 --- a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json +++ b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-operation-parameter-schema.expected.json @@ -55,6 +55,46 @@ "OAS3file_26-request-validator-plugin-path-operation-parameter-schema.yaml" ] }, + { + "id": "04fe9734-c93d-59bc-8723-528a778996c3", + "methods": [ + "GET" + ], + "name": "test-service_sample-group-sample-service-sample-api-v1-get-result_get", + "paths": [ + "~/sample-group/sample-service/sample-api/v1/get-result$" + ], + "plugins": [ + { + "config": { + "parameter_schema": [ + { + "explode": false, + "in": "header", + "name": "env", + "required": true, + "schema": "{\"enum\":[\"qa\"],\"type\":\"string\"}", + "style": "simple" + } + ], + "version": "draft4" + }, + "enabled": true, + "id": "8384b338-2d12-5035-902c-a20fd9ddd6fb", + "name": "request-validator", + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-operation-parameter-schema.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-operation-parameter-schema.yaml" + ] + }, { "headers": { "env": [ @@ -99,6 +139,46 @@ "OAS3_import", "OAS3file_26-request-validator-plugin-path-operation-parameter-schema.yaml" ] + }, + { + "id": "a9452a70-c0fe-5a3b-8446-c329ba5c55e2", + "methods": [ + "OPTIONS" + ], + "name": "test-service_sample-group-sample-service-sample-api-v1-get-result_options", + "paths": [ + "~/sample-group/sample-service/sample-api/v1/get-result$" + ], + "plugins": [ + { + "config": { + "parameter_schema": [ + { + "explode": false, + "in": "header", + "name": "env", + "required": true, + "schema": "{\"enum\":[\"dev\"],\"type\":\"string\"}", + "style": "simple" + } + ], + "version": "draft4" + }, + "enabled": true, + "id": "eb7440df-3f27-5647-bab9-fec82845e2b9", + "name": "request-validator", + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-operation-parameter-schema.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-operation-parameter-schema.yaml" + ] } ], "tags": [ @@ -108,4 +188,4 @@ } ], "upstreams": [] -} +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json index 33d72f8..57479d9 100644 --- a/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json +++ b/openapi2kong/oas3_testfiles/26-request-validator-plugin-path-parameter-schema.expected.json @@ -55,6 +55,46 @@ "OAS3file_26-request-validator-plugin-path-parameter-schema.yaml" ] }, + { + "id": "1a1698ae-e685-55b7-b154-33f4b43706b6", + "methods": [ + "GET" + ], + "name": "test-service_sample-group-sample-service-sample-api-v2-get-result_get", + "paths": [ + "~/sample-group/sample-service/sample-api/v2/get-result$" + ], + "plugins": [ + { + "config": { + "parameter_schema": [ + { + "explode": false, + "in": "header", + "name": "env", + "required": true, + "schema": "{\"enum\":[\"qa\"],\"type\":\"string\"}", + "style": "simple" + } + ], + "version": "draft4" + }, + "enabled": true, + "id": "ddb9f3cc-b11a-58f2-87e0-e47d8eca0242", + "name": "request-validator", + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-parameter-schema.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-parameter-schema.yaml" + ] + }, { "headers": { "env": [ @@ -99,6 +139,46 @@ "OAS3_import", "OAS3file_26-request-validator-plugin-path-parameter-schema.yaml" ] + }, + { + "id": "87bb908e-dde6-5396-926e-98c7bc38683f", + "methods": [ + "OPTIONS" + ], + "name": "test-service_sample-group-sample-service-sample-api-v2-get-result_options", + "paths": [ + "~/sample-group/sample-service/sample-api/v2/get-result$" + ], + "plugins": [ + { + "config": { + "parameter_schema": [ + { + "explode": false, + "in": "header", + "name": "env", + "required": true, + "schema": "{\"enum\":[\"qa\"],\"type\":\"string\"}", + "style": "simple" + } + ], + "version": "draft4" + }, + "enabled": true, + "id": "1da08580-9237-584f-8213-9d64c1a87782", + "name": "request-validator", + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-parameter-schema.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_26-request-validator-plugin-path-parameter-schema.yaml" + ] } ], "tags": [ @@ -108,4 +188,4 @@ } ], "upstreams": [] -} +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json index ffa287b..7c10e56 100644 --- a/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json +++ b/openapi2kong/oas3_testfiles/27-routes-with-optional-headers.expected.json @@ -53,6 +53,23 @@ "OAS3_import", "OAS3file_27-routes-with-optional-headers.yaml" ] + }, + { + "id": "27ea9a0a-216e-5c92-ae0b-8c9aa8493750", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_27-routes-with-optional-headers.yaml" + ] } ], "tags": [ diff --git a/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json b/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json index a9e031f..c07c8b6 100644 --- a/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json +++ b/openapi2kong/oas3_testfiles/28-routes-with-optional-headers-force.expected.json @@ -159,6 +159,23 @@ "OAS3_import", "OAS3file_28-routes-with-optional-headers-force.yaml" ] + }, + { + "id": "27ea9a0a-216e-5c92-ae0b-8c9aa8493750", + "methods": [ + "GET" + ], + "name": "simple-api-overview_get-path2-service", + "paths": [ + "~/path2$" + ], + "plugins": [], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_28-routes-with-optional-headers-force.yaml" + ] } ], "tags": [ diff --git a/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json b/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json index c9e2f8a..0dc74cb 100644 --- a/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json +++ b/openapi2kong/oas3_testfiles/29-routes-with-headers-duplicate-plugin-id.expected.json @@ -83,6 +83,38 @@ "OAS3_import", "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" ] + }, + { + "id": "4773c6b3-8ca7-548b-85eb-4a45ae2be299", + "methods": [ + "POST" + ], + "name": "example-api_exampleoperation", + "paths": [ + "~/api/v1/example$" + ], + "plugins": [ + { + "config": { + "limit": [ + 500 + ] + }, + "enabled": true, + "id": "9770aa9c-6849-5a68-824f-f3b3ae31f2e5", + "name": "rate-limiting-advanced", + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] + } + ], + "regex_priority": 200, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_29-routes-with-headers-duplicate-plugin-id.yaml" + ] } ], "tags": [