diff --git a/bundle/internal/schema/annotations.go b/bundle/internal/schema/annotations.go index d3e105540d..dff95aafa1 100644 --- a/bundle/internal/schema/annotations.go +++ b/bundle/internal/schema/annotations.go @@ -175,8 +175,9 @@ func assignAnnotation(s *jsonschema.Schema, a annotation.Descriptor) { s.MarkdownDescription = convertLinksToAbsoluteUrl(a.MarkdownDescription) s.Title = a.Title - s.Enum = a.Enum - s.EnumDescriptions = buildEnumDescriptions(a.Enum, a.EnumLaunchStages, a.EnumDescriptions) + enum := deduplicateEnumValues(a.Enum) + s.Enum = enum + s.EnumDescriptions = buildEnumDescriptions(enum, a.EnumLaunchStages, a.EnumDescriptions) // Surface launch stage in hover tooltips. Editors only render the standard // description field, so the tag is baked into the text. @@ -185,6 +186,30 @@ func assignAnnotation(s *jsonschema.Schema, a annotation.Descriptor) { } } +func deduplicateEnumValues(enum []any) []any { + if len(enum) < 2 { + return enum + } + + out := make([]any, 0, len(enum)) + for _, value := range enum { + found := false + for _, existing := range out { + if reflect.DeepEqual(existing, value) { + found = true + break + } + } + if !found { + out = append(out, value) + } + } + if len(out) == len(enum) { + return enum + } + return out +} + // buildEnumDescriptions produces the parallel enumDescriptions array VSCode // renders next to each enum value. Each entry combines the launch-stage tag // and the per-value description text. Returns nil when every entry would be diff --git a/bundle/internal/schema/annotations_test.go b/bundle/internal/schema/annotations_test.go index 7caaa0d29d..72998db6bc 100644 --- a/bundle/internal/schema/annotations_test.go +++ b/bundle/internal/schema/annotations_test.go @@ -196,6 +196,19 @@ func TestAssignAnnotationLaunchStage(t *testing.T) { assert.Equal(t, "Type of endpoint.", s.Description) assert.Equal(t, []string{"[Public Preview]", ""}, s.EnumDescriptions) }) + + t.Run("deduplicates enum values before building enum descriptions", func(t *testing.T) { + s := &jsonschema.Schema{} + assignAnnotation(s, annotation.Descriptor{ + Enum: []any{"AWS_SSE_S3", "AWS_SSE_KMS", "AWS_SSE_KMS", "AWS_SSE_S3"}, + EnumDescriptions: map[string]string{ + "AWS_SSE_KMS": "SSE-KMS encryption.", + "AWS_SSE_S3": "SSE-S3 encryption.", + }, + }) + assert.Equal(t, []any{"AWS_SSE_S3", "AWS_SSE_KMS"}, s.Enum) + assert.Equal(t, []string{"SSE-S3 encryption.", "SSE-KMS encryption."}, s.EnumDescriptions) + }) } func TestPrefixWithPreviewTagNoDoubleTag(t *testing.T) { diff --git a/bundle/schema/embed_test.go b/bundle/schema/embed_test.go index 03d2165e42..0dfb7f9684 100644 --- a/bundle/schema/embed_test.go +++ b/bundle/schema/embed_test.go @@ -2,6 +2,7 @@ package schema_test import ( "encoding/json" + "fmt" "testing" "github.com/databricks/cli/bundle/schema" @@ -64,3 +65,46 @@ func TestJsonSchema(t *testing.T) { assert.Contains(t, providers.OneOf[0].Enum, "gitHubEnterprise") assert.Contains(t, providers.OneOf[0].Enum, "bitbucketServer") } + +func TestJsonSchemaEnumsAreUnique(t *testing.T) { + var s any + err := json.Unmarshal(schema.Bytes, &s) + require.NoError(t, err) + + duplicateEnums := duplicateEnumPaths(s, "") + assert.Empty(t, duplicateEnums) +} + +func duplicateEnumPaths(v any, path string) []string { + var duplicates []string + switch v := v.(type) { + case map[string]any: + if enum, ok := v["enum"].([]any); ok { + seen := map[string]struct{}{} + for _, item := range enum { + keyBytes, err := json.Marshal(item) + if err != nil { + panic(err) + } + key := string(keyBytes) + if _, ok := seen[key]; ok { + duplicates = append(duplicates, path) + break + } + seen[key] = struct{}{} + } + } + for key, value := range v { + childPath := key + if path != "" { + childPath = fmt.Sprintf("%s/%s", path, key) + } + duplicates = append(duplicates, duplicateEnumPaths(value, childPath)...) + } + case []any: + for i, value := range v { + duplicates = append(duplicates, duplicateEnumPaths(value, fmt.Sprintf("%s[%d]", path, i))...) + } + } + return duplicates +} diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index dc03d3c9d0..d61500c4a0 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -4706,9 +4706,7 @@ "description": "SSE algorithm to use for encrypting S3 objects", "enum": [ "AWS_SSE_S3", - "AWS_SSE_KMS", - "AWS_SSE_KMS", - "AWS_SSE_S3" + "AWS_SSE_KMS" ] }, {