Skip to content

Commit

Permalink
feat: added support for path-params outside methods for request-valid…
Browse files Browse the repository at this point in the history
…ation (#216)

* feat: added support for path-params outside methods for request-validation.

This change allows the deck file openapi2kong command
to create request validation configuration for path parameters
directly under a route, not nested under an operation/method.
Till now, we supported path parameters nested under a method.

Fixes: #207

* fix: if path and operation params have same name and location,
prioritise operation param

* addressed PR comment

* fix: fixed escaping of curly braces using fix script
  • Loading branch information
Prashansa-K authored Oct 29, 2024
1 parent 475b3aa commit b9e8b45
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@
}
],
"upstreams": []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,4 @@
}
],
"upstreams": []
}
}
Original file line number Diff line number Diff line change
@@ -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/(?<common_param>[^#?/]+)$"
],
"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": []
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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/(?<common_param>[^#?/]+)$"
],
"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": []
}
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion openapi2kong/openapi2kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
38 changes: 28 additions & 10 deletions openapi2kong/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down

0 comments on commit b9e8b45

Please sign in to comment.