diff --git a/builtin/providers/azurerm/structure.go b/builtin/providers/azurerm/structure.go new file mode 100644 index 000000000..47acd631e --- /dev/null +++ b/builtin/providers/azurerm/structure.go @@ -0,0 +1,24 @@ +package azurerm + +import "encoding/json" + +// Takes a value containing JSON string and passes it through +// the JSON parser to normalize it, returns either a parsing +// error or normalized JSON string. +func normalizeJsonString(jsonString interface{}) (string, error) { + var j interface{} + + if jsonString == nil || jsonString.(string) == "" { + return "", nil + } + + s := jsonString.(string) + + err := json.Unmarshal([]byte(s), &j) + if err != nil { + return s, err + } + + bytes, _ := json.Marshal(j) + return string(bytes[:]), nil +} diff --git a/builtin/providers/azurerm/structure_test.go b/builtin/providers/azurerm/structure_test.go new file mode 100644 index 000000000..7ad258333 --- /dev/null +++ b/builtin/providers/azurerm/structure_test.go @@ -0,0 +1,55 @@ +package azurerm + +import "testing" + +func TestNormalizeJsonString(t *testing.T) { + var err error + var actual string + + // Well formatted and valid. + validJson := `{ + "abc": { + "def": 123, + "xyz": [ + { + "a": "ホリネズミ" + }, + { + "b": "1\\n2" + } + ] + } +}` + expected := `{"abc":{"def":123,"xyz":[{"a":"ホリネズミ"},{"b":"1\\n2"}]}}` + + actual, err = normalizeJsonString(validJson) + if err != nil { + t.Fatalf("Expected not to throw an error while parsing JSON, but got: %s", err) + } + + if actual != expected { + t.Fatalf("Got:\n\n%s\n\nExpected:\n\n%s\n", actual, expected) + } + + // Well formatted but not valid, + // missing closing squre bracket. + invalidJson := `{ + "abc": { + "def": 123, + "xyz": [ + { + "a": "1" + } + } + } +}` + actual, err = normalizeJsonString(invalidJson) + if err == nil { + t.Fatalf("Expected to throw an error while parsing JSON, but got: %s", err) + } + + // We expect the invalid JSON to be shown back to us again. + if actual != invalidJson { + t.Fatalf("Got:\n\n%s\n\nExpected:\n\n%s\n", expected, invalidJson) + } +} diff --git a/builtin/providers/azurerm/validators.go b/builtin/providers/azurerm/validators.go new file mode 100644 index 000000000..d6fa6a943 --- /dev/null +++ b/builtin/providers/azurerm/validators.go @@ -0,0 +1,10 @@ +package azurerm + +import "fmt" + +func validateJsonString(v interface{}, k string) (ws []string, errors []error) { + if _, err := normalizeJsonString(v); err != nil { + errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) + } + return +} diff --git a/builtin/providers/azurerm/validators_test.go b/builtin/providers/azurerm/validators_test.go new file mode 100644 index 000000000..66984be06 --- /dev/null +++ b/builtin/providers/azurerm/validators_test.go @@ -0,0 +1,58 @@ +package azurerm + +import "testing" + +func TestValidateJsonString(t *testing.T) { + type testCases struct { + Value string + ErrCount int + } + + invalidCases := []testCases{ + { + Value: `{0:"1"}`, + ErrCount: 1, + }, + { + Value: `{'abc':1}`, + ErrCount: 1, + }, + { + Value: `{"def":}`, + ErrCount: 1, + }, + { + Value: `{"xyz":[}}`, + ErrCount: 1, + }, + } + + for _, tc := range invalidCases { + _, errors := validateJsonString(tc.Value, "json") + if len(errors) != tc.ErrCount { + t.Fatalf("Expected %q to trigger a validation error.", tc.Value) + } + } + + validCases := []testCases{ + { + Value: ``, + ErrCount: 0, + }, + { + Value: `{}`, + ErrCount: 0, + }, + { + Value: `{"abc":["1","2"]}`, + ErrCount: 0, + }, + } + + for _, tc := range validCases { + _, errors := validateJsonString(tc.Value, "json") + if len(errors) != tc.ErrCount { + t.Fatalf("Expected %q not to trigger a validation error.", tc.Value) + } + } +}