diff --git a/openapi2kong/oas3_testfiles/18-request-validator-plugin-path-params-outside-ops.expected.json b/openapi2kong/oas3_testfiles/18-request-validator-plugin-path-params-outside-ops.expected.json new file mode 100644 index 0000000..7206d79 --- /dev/null +++ b/openapi2kong/oas3_testfiles/18-request-validator-plugin-path-params-outside-ops.expected.json @@ -0,0 +1,70 @@ +{ + "_format_version": "3.0", + "services": [ + { + "host": "backend.com", + "id": "730d612d-914b-5fe8-8ead-e6aa654318ef", + "name": "example", + "path": "/path", + "plugins": [], + "port": 80, + "protocol": "http", + "routes": [ + { + "id": "bee0be08-646a-562a-91b9-71737169585b", + "methods": [ + "GET" + ], + "name": "example_test-common-param-common-param_get", + "paths": [ + "~/test/common-param/(?\u003ccommon_param\u003e[^#?/]+)$" + ], + "plugins": [ + { + "config": { + "parameter_schema": [ + { + "explode": false, + "in": "path", + "name": "common_param", + "required": true, + "schema": "{\"type\":\"integer\"}", + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "metadata", + "required": false, + "schema": "{\"type\":\"boolean\"}", + "style": "form" + } + ], + "verbose_response": true, + "version": "draft4" + }, + "enabled": true, + "id": "3bcb9a87-847d-5ccf-93dc-b2aa1b32b77d", + "name": "request-validator", + "tags": [ + "OAS3_import", + "OAS3file_18-request-validator-plugin-path-params-outside-ops.yaml" + ] + } + ], + "regex_priority": 100, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_18-request-validator-plugin-path-params-outside-ops.yaml" + ] + } + ], + "tags": [ + "OAS3_import", + "OAS3file_18-request-validator-plugin-path-params-outside-ops.yaml" + ] + } + ], + "upstreams": [] +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/18-request-validator-plugin-path-params-outside-ops.yaml b/openapi2kong/oas3_testfiles/18-request-validator-plugin-path-params-outside-ops.yaml new file mode 100644 index 0000000..4320824 --- /dev/null +++ b/openapi2kong/oas3_testfiles/18-request-validator-plugin-path-params-outside-ops.yaml @@ -0,0 +1,36 @@ +# When the request-validator is added with path and operation parameters, +# the generator should automatically generate it. + +openapi: 3.0.2 + +info: + title: Example + version: 1.0.0 + +servers: + - url: http://backend.com/path + +x-kong-plugin-request-validator: {} + +paths: + /test/common-param/{common-param}: + parameters: + - in: path + name: common-param + schema: + type: integer + required: true + get: + parameters: + - in: query + name: metadata + schema: + type: boolean + required: false + responses: + '200': + description: OK + x-kong-plugin-request-validator: + enabled: true + config: + verbose_response: true diff --git a/openapi2kong/oas3_testfiles/19-request-validator-plugin-op-params-override-path-params.expected.json b/openapi2kong/oas3_testfiles/19-request-validator-plugin-op-params-override-path-params.expected.json new file mode 100644 index 0000000..f062b55 --- /dev/null +++ b/openapi2kong/oas3_testfiles/19-request-validator-plugin-op-params-override-path-params.expected.json @@ -0,0 +1,62 @@ +{ + "_format_version": "3.0", + "services": [ + { + "host": "backend.com", + "id": "730d612d-914b-5fe8-8ead-e6aa654318ef", + "name": "example", + "path": "/path", + "plugins": [], + "port": 80, + "protocol": "http", + "routes": [ + { + "id": "bee0be08-646a-562a-91b9-71737169585b", + "methods": [ + "GET" + ], + "name": "example_test-common-param-common-param_get", + "paths": [ + "~/test/common-param/(?\u003ccommon_param\u003e[^#?/]+)$" + ], + "plugins": [ + { + "config": { + "parameter_schema": [ + { + "explode": false, + "in": "path", + "name": "common_param", + "required": true, + "schema": "{\"type\":\"integer\"}", + "style": "simple" + } + ], + "verbose_response": true, + "version": "draft4" + }, + "enabled": true, + "id": "3bcb9a87-847d-5ccf-93dc-b2aa1b32b77d", + "name": "request-validator", + "tags": [ + "OAS3_import", + "OAS3file_19-request-validator-plugin-op-params-override-path-params.yaml" + ] + } + ], + "regex_priority": 100, + "strip_path": false, + "tags": [ + "OAS3_import", + "OAS3file_19-request-validator-plugin-op-params-override-path-params.yaml" + ] + } + ], + "tags": [ + "OAS3_import", + "OAS3file_19-request-validator-plugin-op-params-override-path-params.yaml" + ] + } + ], + "upstreams": [] +} \ No newline at end of file diff --git a/openapi2kong/oas3_testfiles/19-request-validator-plugin-op-params-override-path-params.yaml b/openapi2kong/oas3_testfiles/19-request-validator-plugin-op-params-override-path-params.yaml new file mode 100644 index 0000000..8c91fe0 --- /dev/null +++ b/openapi2kong/oas3_testfiles/19-request-validator-plugin-op-params-override-path-params.yaml @@ -0,0 +1,36 @@ +# When the request-validator is added with path and operation parameters, +# with both sharing same name and location, operation parameters override the path parameters. + +openapi: 3.0.2 + +info: + title: Example + version: 1.0.0 + +servers: + - url: http://backend.com/path + +x-kong-plugin-request-validator: {} + +paths: + /test/common-param/{common-param}: + parameters: + - in: path + name: common-param + schema: + type: string + required: true + get: + parameters: + - in: path + name: common-param + schema: + type: integer + required: true + responses: + '200': + description: OK + x-kong-plugin-request-validator: + enabled: true + config: + verbose_response: true diff --git a/openapi2kong/openapi2kong.go b/openapi2kong/openapi2kong.go index 98f07ff..84c32a4 100644 --- a/openapi2kong/openapi2kong.go +++ b/openapi2kong/openapi2kong.go @@ -1027,7 +1027,7 @@ func Convert(content []byte, opts O2kOptions) (map[string]interface{}, error) { // Extract the request-validator config from the plugin list, generate it and reinsert operationValidatorConfig, operationPluginList = getValidatorPlugin(operationPluginList, pathValidatorConfig) - validatorPlugin := generateValidatorPlugin(operationValidatorConfig, operation, opts.UUIDNamespace, + validatorPlugin := generateValidatorPlugin(operationValidatorConfig, operation, pathitem, opts.UUIDNamespace, operationBaseName, opts.SkipID, opts.InsoCompat) operationPluginList = insertPlugin(operationPluginList, validatorPlugin) diff --git a/openapi2kong/validator.go b/openapi2kong/validator.go index b4d2f9e..8f9dc77 100644 --- a/openapi2kong/validator.go +++ b/openapi2kong/validator.go @@ -33,19 +33,37 @@ func getDefaultParamStyle(givenStyle string, paramType string) string { // generateParameterSchema returns the given schema if there is one, a generated // schema if it was specified, or nil if there is none. // Parameters include path, query, and headers -func generateParameterSchema(operation *v3.Operation, insoCompat bool) []map[string]interface{} { - parameters := operation.Parameters - if parameters == nil { +func generateParameterSchema(operation *v3.Operation, path *v3.PathItem, insoCompat bool) []map[string]interface{} { + pathParameters := path.Parameters + operationParameters := operation.Parameters + if pathParameters == nil && operationParameters == nil { return nil } - if len(parameters) == 0 { + totalLength := len(pathParameters) + len(operationParameters) + if totalLength == 0 { return nil } - result := make([]map[string]interface{}, len(parameters)) + combinedParameters := make([]*v3.Parameter, 0, totalLength) + + for _, pathParam := range pathParameters { + for _, opParam := range operationParameters { + // If path parameter and operation parameter share the same name and location + // operation parameter overrides the path parameter. Thus, if this check passes, + // Then we add the path param, else we skip it. + if pathParam.Name != opParam.Name && pathParam.In != opParam.In { + combinedParameters = append(combinedParameters, pathParam) + } + } + } + + combinedParameters = append(combinedParameters, operationParameters...) + + result := make([]map[string]interface{}, len(combinedParameters)) i := 0 - for _, parameter := range parameters { + + for _, parameter := range combinedParameters { if parameter != nil { style := getDefaultParamStyle(parameter.Style, parameter.In) @@ -160,16 +178,16 @@ func generateContentTypes(operation *v3.Operation) []string { // generateValidatorPlugin generates the validator plugin configuration, based // on the JSON snippet, and the OAS inputs. This can return nil -func generateValidatorPlugin(configJSON []byte, operation *v3.Operation, +func generateValidatorPlugin(operationConfigJSON []byte, operation *v3.Operation, path *v3.PathItem, uuidNamespace uuid.UUID, baseName string, skipID bool, insoCompat bool, ) *map[string]interface{} { - if len(configJSON) == 0 { + if len(operationConfigJSON) == 0 { return nil } logbasics.Debug("generating validator plugin", "operation", baseName) var pluginConfig map[string]interface{} - _ = json.Unmarshal(configJSON, &pluginConfig) + _ = json.Unmarshal(operationConfigJSON, &pluginConfig) // create a new ID here based on the operation if !skipID { @@ -183,7 +201,7 @@ func generateValidatorPlugin(configJSON []byte, operation *v3.Operation, } if config["parameter_schema"] == nil { - parameterSchema := generateParameterSchema(operation, insoCompat) + parameterSchema := generateParameterSchema(operation, path, insoCompat) if parameterSchema != nil { config["parameter_schema"] = parameterSchema config["version"] = JSONSchemaVersion