From 2e45db37698571a4b5946a5a5c78f92481e49c52 Mon Sep 17 00:00:00 2001 From: Shawn Hurley Date: Wed, 20 Mar 2024 09:57:57 -0400 Subject: [PATCH] :bug: handle correct structs for protobof init calls (#541) Adding validation of the keys in provider-specific config to only be strings Adding test cases to validate both JSON and yaml provider settings files will work. fixes #501 Signed-off-by: Shawn Hurley --- provider/provider.go | 82 +++++++++++++++++++ provider/provider_test.go | 75 +++++++++++++++++ .../testdata/provider_settings_invalid.yaml | 21 +++++ .../provider_settings_nested_types.json | 23 ++++++ .../testdata/provider_settings_simple.yaml | 20 +++++ 5 files changed, 221 insertions(+) create mode 100644 provider/testdata/provider_settings_invalid.yaml create mode 100644 provider/testdata/provider_settings_nested_types.json create mode 100644 provider/testdata/provider_settings_simple.yaml diff --git a/provider/provider.go b/provider/provider.go index 4421d0b4..6a570b33 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -170,6 +170,12 @@ func GetConfig(filepath string) ([]Config, error) { if ic.Proxy == nil { ic.Proxy = c.Proxy } + newConfig, err := validateAndUpdateProviderSpecificConfig(ic.ProviderSpecificConfig) + if err != nil { + return configs, err + } + ic.ProviderSpecificConfig = newConfig + } } if !foundBuiltin { @@ -185,6 +191,82 @@ func GetConfig(filepath string) ([]Config, error) { } +func validateAndUpdateProviderSpecificConfig(oldPSC map[string]interface{}) (map[string]interface{}, error) { + newPSC := map[string]interface{}{} + for k, v := range oldPSC { + if old, ok := v.(map[interface{}]interface{}); ok { + new, err := validateUpdateInternalProviderConfig(old) + if err != nil { + return nil, err + } + newPSC[k] = new + continue + } + if oldList, ok := v.([]interface{}); ok { + newList, err := validateUpdateListProviderConfig(oldList) + if err != nil { + return nil, err + } + newPSC[k] = newList + continue + } + newPSC[k] = v + } + return newPSC, nil +} + +func validateUpdateListProviderConfig(old []interface{}) ([]interface{}, error) { + new := []interface{}{} + for _, v := range old { + if oldV, ok := v.(map[interface{}]interface{}); ok { + newMap, err := validateUpdateInternalProviderConfig(oldV) + if err != nil { + return nil, err + } + new = append(new, newMap) + continue + } + if oldList, ok := v.([]interface{}); ok { + newList, err := validateUpdateListProviderConfig(oldList) + if err != nil { + return nil, err + } + new = append(new, newList) + continue + } + new = append(new, v) + } + return new, nil +} + +func validateUpdateInternalProviderConfig(old map[interface{}]interface{}) (map[string]interface{}, error) { + new := map[string]interface{}{} + for k, v := range old { + s, ok := k.(string) + if !ok { + return nil, fmt.Errorf("provider specific config must only have keys that strings") + } + if o, ok := v.(map[interface{}]interface{}); ok { + new, err := validateUpdateInternalProviderConfig(o) + if err != nil { + return nil, err + } + new[s] = new + continue + } + if oldList, ok := v.([]interface{}); ok { + newList, err := validateUpdateListProviderConfig(oldList) + if err != nil { + return nil, err + } + new[s] = newList + continue + } + new[s] = v + } + return new, nil +} + func validateProviderName(configs []Config) error { providerNames := make(map[string]bool) for _, config := range configs { diff --git a/provider/provider_test.go b/provider/provider_test.go index 9fe847fd..e9e638db 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -2,6 +2,7 @@ package provider import ( "context" + "fmt" "reflect" "testing" @@ -337,3 +338,77 @@ func Test_deduplication(t *testing.T) { } } + +func Test_GetConfigs(t *testing.T) { + tests := []struct { + title string + testdataFile string + expectedProviderSpecificConfig map[string]interface{} + shouldErr bool + }{ + { + title: "testnested", + testdataFile: "testdata/provider_settings_nested_types.json", + expectedProviderSpecificConfig: map[string]interface{}{ + "lspServerName": "generic", + "lspServerPath": "/root/go/bin/gopls", + "lspServerArgs": []interface{}{"string"}, + "lspServerInitializationOptions": "", + "workspaceFolders": []interface{}{"file:///analyzer-lsp/examples/golang"}, + "dependencyFolders": []interface{}{}, + "groupVersionKinds": []interface{}{ + map[string]interface{}{"group": "apps", "version": "v1", "kind": "Deployment"}, + }, + "object": map[string]interface{}{"nestedObject": "object"}, + "dependencyProviderPath": "/usr/bin/golang-dependency-provider", + }, + }, + { + title: "test nested yaml", + testdataFile: "testdata/provider_settings_simple.yaml", + expectedProviderSpecificConfig: map[string]interface{}{ + "lspServerName": "generic", + "lspServerPath": "/root/go/bin/gopls", + "lspServerArgs": []interface{}{"string"}, + "lspServerInitializationOptions": "", + "workspaceFolders": []interface{}{"file:///analyzer-lsp/examples/golang"}, + "dependencyFolders": []interface{}{}, + "groupVersionKinds": []interface{}{ + map[string]interface{}{"group": "apps", "version": "v1", "kind": "Deployment"}, + }, + "object": map[string]interface{}{"nestedObject": "object"}, + "dependencyProviderPath": "/usr/bin/golang-dependency-provider", + }, + }, + { + title: "test yaml int keys", + testdataFile: "testdata/provider_settings_invalid.yaml", + shouldErr: true, + }, + } + for _, tc := range tests { + t.Run(tc.title, func(t *testing.T) { + config, err := GetConfig(tc.testdataFile) + if err != nil && !tc.shouldErr { + t.Fatalf("got error: %v", err) + } + if err != nil && tc.shouldErr { + return + } + // This is true because of the builtin config that will be added if not there + if len(config) != 2 { + t.Fatalf("got config longer than one: %v", len(config)) + } + c := config[0] + if len(c.InitConfig) != 1 { + t.Fatalf("got init config longer than one: %v", len(c.InitConfig)) + } + pc := c.InitConfig[0] + if !reflect.DeepEqual(pc.ProviderSpecificConfig, tc.expectedProviderSpecificConfig) { + fmt.Printf("\n%#v", pc.ProviderSpecificConfig) + fmt.Printf("\n%#v\n", tc.expectedProviderSpecificConfig) + t.Fatalf("Got config is different than expected config") + } + }) + } +} diff --git a/provider/testdata/provider_settings_invalid.yaml b/provider/testdata/provider_settings_invalid.yaml new file mode 100644 index 00000000..ab70ad9b --- /dev/null +++ b/provider/testdata/provider_settings_invalid.yaml @@ -0,0 +1,21 @@ +- name: "go" + binaryPath: "/usr/bin/generic-external-provider" + initConfig: + - analysisMode: "full" + providerSpecificConfig: + lspServerName: "generic" + lspServerPath: "/root/go/bin/gopls" + lspServerArgs: + - "string" + lspServerInitializationOptions: "" + workspaceFolders: + - "file:///analyzer-lsp/examples/golang" + dependencyFolders: [] + groupVersionKinds: + - group: "apps" + version: "v1" + kind: "Deployment" + 1: "newThing" + object: + nestedObject: "object" + dependencyProviderPath: "/usr/bin/golang-dependency-provider" \ No newline at end of file diff --git a/provider/testdata/provider_settings_nested_types.json b/provider/testdata/provider_settings_nested_types.json new file mode 100644 index 00000000..6faa0b44 --- /dev/null +++ b/provider/testdata/provider_settings_nested_types.json @@ -0,0 +1,23 @@ + +[ + { + "name": "go", + "binaryPath": "/usr/bin/generic-external-provider", + "initConfig": [{ + "analysisMode": "full", + "providerSpecificConfig": { + "lspServerName": "generic", + "lspServerPath": "/root/go/bin/gopls", + "lspServerArgs": ["string"], + "lspServerInitializationOptions": "", + + "workspaceFolders": ["file:///analyzer-lsp/examples/golang"], + "dependencyFolders": [], + "groupVersionKinds": [{"group": "apps", "version": "v1", "kind": "Deployment"}], + "object": {"nestedObject": "object"}, + + "dependencyProviderPath": "/usr/bin/golang-dependency-provider" + } + }] + } +] \ No newline at end of file diff --git a/provider/testdata/provider_settings_simple.yaml b/provider/testdata/provider_settings_simple.yaml new file mode 100644 index 00000000..1c97818d --- /dev/null +++ b/provider/testdata/provider_settings_simple.yaml @@ -0,0 +1,20 @@ +- name: "go" + binaryPath: "/usr/bin/generic-external-provider" + initConfig: + - analysisMode: "full" + providerSpecificConfig: + lspServerName: "generic" + lspServerPath: "/root/go/bin/gopls" + lspServerArgs: + - "string" + lspServerInitializationOptions: "" + workspaceFolders: + - "file:///analyzer-lsp/examples/golang" + dependencyFolders: [] + groupVersionKinds: + - group: "apps" + version: "v1" + kind: "Deployment" + object: + nestedObject: "object" + dependencyProviderPath: "/usr/bin/golang-dependency-provider" \ No newline at end of file