Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(utils): allow choosing what fields to fill #466

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions kong/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,13 @@ func backfillResultConfigMap(res Configuration, path []string, configValue inter
return nil
}

type FillRecordOptions struct {
FillDefaults bool
FillAuto bool
}

// fills the config record with default values
// if oldConfig is provided, copies auto fields from it, into config
func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Configuration) Configuration {
func fillConfigRecord(schema gjson.Result, config Configuration, opts FillRecordOptions) Configuration {
res := config.DeepCopy()
configFields := schema.Get("fields")
// Fetch deprecated fields
Expand All @@ -297,7 +301,7 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi
}

if fname == "config" {
newConfig := fillConfigRecord(value.Get(fname), config, oldConfig)
newConfig := fillConfigRecord(value.Get(fname), config, opts)
res = newConfig
return true
}
Expand Down Expand Up @@ -350,7 +354,7 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi
default:
fieldConfig = subConfig.(map[string]interface{})
}
newSubConfig := fillConfigRecord(value.Get(fname), fieldConfig, oldConfig)
newSubConfig := fillConfigRecord(value.Get(fname), fieldConfig, opts)
res[fname] = map[string]interface{}(newSubConfig)
return true
}
Expand All @@ -370,7 +374,7 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi
for i, configRecord := range subConfigArray {
// Check if element is of type record, if it is, set default values by recursively calling `fillConfigRecord`
if configRecordMap, ok := configRecord.(map[string]interface{}); ok {
processedConfigRecord := fillConfigRecord(value.Get(fname).Get("elements"), configRecordMap, oldConfig)
processedConfigRecord := fillConfigRecord(value.Get(fname).Get("elements"), configRecordMap, opts)
processedSubConfigArray[i] = processedConfigRecord
continue
}
Expand All @@ -384,17 +388,17 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi
}
}

// handle `auto` fields by copying them from oldConfig if they don't have
// a value. When fields marked as `auto` are passed to kong without a value
// they are auto generated by the gateway. In that case, comparing with
// "defaults" doesn't work, so in order to compute a correct diff they must
// be copied from the oldConfig.
auto := value.Get(fname + ".auto")
if auto.Exists() && auto.Bool() && oldConfig != nil {
if v, ok := oldConfig[fname]; ok {
res[fname] = v
return true
}
isAuto := auto.Exists() && auto.Bool()

// if this is an auto field and we are not filling auto fields, skip
if !opts.FillAuto && isAuto {
return true
}

// if this is a normal field and we are not filling defaults, skip
if !opts.FillDefaults && !isAuto {
return true
}

// Check if the record has a default value for the specified field.
Expand Down Expand Up @@ -680,7 +684,7 @@ func FillEntityDefaults(entity interface{}, schema Schema) error {
return nil
}

func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interface{}, oldPlugin *Plugin) error {
func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interface{}, opts FillRecordOptions) error {
jsonb, err := json.Marshal(&schema)
if err != nil {
return err
Expand All @@ -694,11 +698,7 @@ func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interf
plugin.Config = make(Configuration)
}

var oldConfig Configuration
if oldPlugin != nil {
oldConfig = oldPlugin.Config
}
plugin.Config = fillConfigRecord(configSchema, plugin.Config, oldConfig)
plugin.Config = fillConfigRecord(configSchema, plugin.Config, opts)
if plugin.Protocols == nil {
plugin.Protocols = getDefaultProtocols(gjsonSchema)
}
Expand All @@ -711,13 +711,13 @@ func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interf
// FillPluginsDefaults ingests plugin's defaults from its schema.
// Takes in a plugin struct and mutate it in place.
func FillPluginsDefaults(plugin *Plugin, schema Schema) error {
return fillConfigRecordDefaultsAutoFields(plugin, schema, nil)
return fillConfigRecordDefaultsAutoFields(plugin, schema, FillRecordOptions{
FillDefaults: true,
FillAuto: true,
})
}

// same as FillPluginsDefaults but also fills auto fields.
// `oldPlugin` (which repsesents the plugin from the old/existing Kong config
// is used to copy auto fields from it when they are not set in `plugin“.
// keeping both for compatibility: these utils are public
func FillPluginsDefaultsAutoFields(plugin *Plugin, schema map[string]interface{}, oldPlugin *Plugin) error {
return fillConfigRecordDefaultsAutoFields(plugin, schema, oldPlugin)
// same as FillPluginsDefaults but allows configuring whether to fill defaults and auto fields.
func FillPluginsDefaultsWithOpts(plugin *Plugin, schema map[string]interface{}, opts FillRecordOptions) error {
return fillConfigRecordDefaultsAutoFields(plugin, schema, opts)
}
79 changes: 63 additions & 16 deletions kong/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1836,7 +1836,10 @@ func Test_fillConfigRecord(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
configSchema, err := getConfigSchema(tc.schema)
require.NoError(t, err)
config := fillConfigRecord(configSchema, tc.config, nil)
config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{
FillDefaults: true,
FillAuto: true,
})
require.NotNil(t, config)
if diff := cmp.Diff(config, tc.expected); diff != "" {
t.Errorf("unexpected diff:\n%s", diff)
Expand Down Expand Up @@ -1939,19 +1942,25 @@ const fillConfigRecordTestSchemaWithAutoFields = `{
"type": "record",
"fields": [
{
"foo_string": {
"default_string": {
"type": "string",
"default": "abc"
}
},
{
"auto_string_1": {
"type": "string",
"auto": true
}
},
{
"bar_string": {
"auto_string_2": {
"type": "string",
"auto": true
}
},
{
"baz_string": {
"auto_string_3": {
"type": "string",
"auto": true
}
Expand Down Expand Up @@ -2021,7 +2030,10 @@ func Test_fillConfigRecord_shorthand_fields(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
configSchema, err := getConfigSchema(tc.schema)
require.NoError(t, err)
config := fillConfigRecord(configSchema, tc.config, nil)
config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{
FillDefaults: true,
FillAuto: true,
})
require.NotNil(t, config)
if diff := cmp.Diff(config, tc.expected); diff != "" {
t.Errorf("unexpected diff:\n%s", diff)
Expand All @@ -2030,23 +2042,22 @@ func Test_fillConfigRecord_shorthand_fields(t *testing.T) {
}
}

func Test_fillConfigRecord_auto_fields(t *testing.T) {
func Test_fillConfigRecord_defaults_only(t *testing.T) {
tests := []struct {
name string
schema gjson.Result
config Configuration
expected Configuration
}{
{
name: "fills auto fields with values from oldConfig",
name: "fills defaults with opts to fill defaults only",
schema: gjson.Parse(fillConfigRecordTestSchemaWithAutoFields),
config: Configuration{
"baz_string": "789",
"auto_string_3": "789",
},
expected: Configuration{
"foo_string": "123",
"bar_string": "456",
"baz_string": "789",
"default_string": "abc",
"auto_string_3": "789",
},
},
}
Expand All @@ -2055,12 +2066,48 @@ func Test_fillConfigRecord_auto_fields(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
configSchema, err := getConfigSchema(tc.schema)
require.NoError(t, err)
oldConfig := Configuration{
"foo_string": "123",
"bar_string": "456",
"baz_string": "000",
config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{
FillDefaults: true,
FillAuto: false,
})
require.NotNil(t, config)
if diff := cmp.Diff(config, tc.expected); diff != "" {
t.Errorf("unexpected diff:\n%s", diff)
}
config := fillConfigRecord(configSchema, tc.config, oldConfig)
})
}
}

func Test_fillConfigRecord_auto_only(t *testing.T) {
tests := []struct {
name string
schema gjson.Result
config Configuration
expected Configuration
}{
{
name: "fills defaults with opts to fill auto only",
schema: gjson.Parse(fillConfigRecordTestSchemaWithAutoFields),
config: Configuration{
"auto_string_3": "789",
},
expected: Configuration{
"auto_string_1": nil,
"auto_string_2": nil,
"auto_string_3": "789",
// defalt_string missing
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
configSchema, err := getConfigSchema(tc.schema)
require.NoError(t, err)
config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{
FillDefaults: false,
FillAuto: true,
})
require.NotNil(t, config)
if diff := cmp.Diff(config, tc.expected); diff != "" {
t.Errorf("unexpected diff:\n%s", diff)
Expand Down
Loading