Skip to content

Commit

Permalink
fix(utils): support record-level default schema values (#431)
Browse files Browse the repository at this point in the history
Before this change, during the process of filling default configuration
values, if a record existed in the schema with a structure similar to the
following:
```
{
	"default": {
			"some_field": "record_level_default"
	},
	"fields": [
		{
			"some_field": {
				"type": "string",
				"default": "field_level_default"
			}
		}
	]
}
```
the value `record_level_default` would be ignored and the field set to
the `field_level_default` value. Similarly, in case of no field-level
default, the field would be set to `null`.

This commit modifies the utils/fillConfigRecord logic to assign fields
with record-level default values when available.

Example of default configuration values that were previously ignored:
https://github.com/Kong/kong/blob/8fe14097a17cf904676672fc8cdaabe5e02e4a2d/kong/plugins/opentelemetry/schema.lua#L49
https://github.com/Kong/kong/blob/8fe14097a17cf904676672fc8cdaabe5e02e4a2d/kong/plugins/opentelemetry/schema.lua#L90
  • Loading branch information
samugi authored Apr 3, 2024
1 parent f34633e commit 2b721b4
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 1 deletion.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Table of Contents

- [v0.53.0](#v0530)
- [v0.52.0](#v0520)
- [v0.51.0](#v0510)
- [v0.50.0](#v0500)
Expand Down Expand Up @@ -64,6 +65,13 @@
- [0.2.0](#020)
- [0.1.0](#010)

## [v0.53.0]

> Release date:
- Support record-level default schema values.
[#431](https://github.com/Kong/go-kong/pull/431)

## [v0.52.0]

> Release date: 2024/03/26
Expand Down Expand Up @@ -861,6 +869,8 @@ authentication credentials in Kong.
releases of Kong since every release of Kong is introducing breaking changes
to the Admin API.

[v0.53.0]: https://github.com/Kong/go-kong/compare/v0.52.0...v0.53.0
[v0.52.0]: https://github.com/Kong/go-kong/compare/v0.51.0...v0.52.0
[v0.51.0]: https://github.com/Kong/go-kong/compare/v0.50.0...v0.51.0
[v0.50.0]: https://github.com/Kong/go-kong/compare/v0.49.0...v0.50.0
[v0.49.0]: https://github.com/Kong/go-kong/compare/v0.48.0...v0.49.0
Expand Down
11 changes: 10 additions & 1 deletion kong/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ func getConfigSchema(schema gjson.Result) (gjson.Result, error) {
func fillConfigRecord(schema gjson.Result, config Configuration) Configuration {
res := config.DeepCopy()
value := schema.Get("fields")
defaultRecordValue := schema.Get("default")

value.ForEach(func(_, value gjson.Result) bool {
// get the key name
Expand Down Expand Up @@ -308,7 +309,15 @@ func fillConfigRecord(schema gjson.Result, config Configuration) Configuration {
}
}
}
value = value.Get(fname + ".default")

// Check if the record has a default value for the specified field.
// If so, use it. If not, fall back to the default value of the field itself.
if defaultRecordValue.Exists() && defaultRecordValue.Get(fname).Exists() {
value = defaultRecordValue.Get(fname)
} else {
value = value.Get(fname + ".default")
}

if value.Exists() {
res[fname] = value.Value()
} else {
Expand Down
160 changes: 160 additions & 0 deletions kong/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,91 @@ const AcmeSchema = `{
]
}`

const defaultRecordSchema = `{
"fields": [
{
"config": {
"fields": [
{
"endpoint": {
"description": "endpoint",
"referenceable": true,
"required": true,
"type": "string"
}
},
{
"queue": {
"default": {
"max_batch_size": 200
},
"fields": [
{
"max_batch_size": {
"between": [
1,
1000000
],
"default": 1,
"type": "integer"
}
},
{
"max_coalescing_delay": {
"between": [
0,
3600
],
"default": 1,
"type": "number"
}
}
],
"required": true,
"type": "record"
}
},
{
"propagation": {
"default": {
"default_format": "w3c"
},
"fields": [
{
"extract": {
"elements": {
"one_of": [
"w3c",
"b3"
],
"type": "string"
},
"type": "array"
}
},
{
"default_format": {
"one_of": [
"w3c",
"b3"
],
"required": true,
"type": "string"
}
}
],
"required": true,
"type": "record"
}
}
],
"required": true,
"type": "record"
}
}
]
}`

func TestStringArrayToString(t *testing.T) {
assert := assert.New(t)

Expand Down Expand Up @@ -2213,6 +2298,81 @@ func Test_FillPluginsDefaults_Acme(t *testing.T) {
}
}

func Test_FillPluginsDefaults_DefaultRecord(t *testing.T) {
client, err := NewTestClient(nil, nil)
require.NoError(t, err)
require.NotNil(t, client)

tests := []struct {
name string
plugin *Plugin
expected *Plugin
}{
{
name: "record defaults take precedence over fields defaults",
plugin: &Plugin{
Config: Configuration{
"endpoint": "http://test.test:4317",
},
},
expected: &Plugin{
Config: Configuration{
"endpoint": "http://test.test:4317",
"propagation": map[string]interface{}{
"default_format": string("w3c"), // from record defaults
"extract": nil, // from field defaults
},
"queue": map[string]interface{}{
"max_batch_size": float64(200), // from record defaults
"max_coalescing_delay": float64(1), // from field defaults
},
},
},
},
{
name: "configured values take precedence over record defaults",
plugin: &Plugin{
Config: Configuration{
"endpoint": "http://test.test:4317",
"propagation": map[string]interface{}{
"default_format": "b3",
},
"queue": map[string]interface{}{
"max_batch_size": 123,
},
},
},
expected: &Plugin{
Config: Configuration{
"endpoint": "http://test.test:4317",
"propagation": map[string]interface{}{
"default_format": string("b3"),
"extract": nil, // from field defaults
},
"queue": map[string]interface{}{
"max_batch_size": float64(123),
"max_coalescing_delay": float64(1), // from field defaults
},
},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
plugin := tc.plugin
var fullSchema map[string]interface{}
require.NoError(t, json.Unmarshal([]byte(defaultRecordSchema), &fullSchema))
require.NotNil(t, fullSchema)
assert.NoError(t, FillPluginsDefaults(plugin, fullSchema))
opts := cmpopts.IgnoreFields(*plugin, "Enabled", "Protocols")
if diff := cmp.Diff(plugin, tc.expected, opts); diff != "" {
t.Errorf(diff)
}
})
}
}

const NonEmptyDefaultArrayFieldSchema = `{
"fields": [
{
Expand Down

0 comments on commit 2b721b4

Please sign in to comment.