Skip to content
Open
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
14 changes: 8 additions & 6 deletions bundle/internal/schema/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,16 @@ func assignAnnotation(s *jsonschema.Schema, a annotation.Descriptor) {
s.DeprecationMessage = a.DeprecationMessage
}

// Private-preview fields are hidden from completions and surfaced to
// downstream codegen via the launch stage: pydabs reads
// x-databricks-launch-stage from jsonschema.json to mark these fields
// experimental. Only the private-preview stage is emitted into the published
// schema — nothing consumes the others there; they surface only as the
// description prefix below and the per-value enumDescriptions labels.
// Private-preview fields are hidden from completions.
if a.LaunchStage == clijson.LaunchStagePrivatePreview {
s.DoNotSuggest = true
}

// Emit the launch stage for every field the contract stamps (GA included) so
// downstream codegen can read each field's stability, not just private
// preview. Fields the contract leaves unstamped stay empty. pydabs reads
// x-databricks-launch-stage from jsonschema.json.
if a.LaunchStage != "" {
s.LaunchStage = string(a.LaunchStage)
}

Expand Down
66 changes: 34 additions & 32 deletions bundle/internal/schema/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,37 +151,35 @@ func TestStalePlaceholderDoesNotShadowMergedDescription(t *testing.T) {
}

func TestAssignAnnotationLaunchStage(t *testing.T) {
t.Run("public preview prefixes description and stays suggestible", func(t *testing.T) {
s := &jsonschema.Schema{}
assignAnnotation(s, annotation.Descriptor{
Description: "Target QPS for the endpoint.",
LaunchStage: "PUBLIC_PREVIEW",
})
assert.Equal(t, "[Public Preview] Target QPS for the endpoint.", s.Description)
assert.False(t, s.DoNotSuggest)
assert.Empty(t, s.LaunchStage)
})

t.Run("public beta prefixes description", func(t *testing.T) {
s := &jsonschema.Schema{}
assignAnnotation(s, annotation.Descriptor{
Description: "A field.",
LaunchStage: "PUBLIC_BETA",
// Each stamped stage emits x-databricks-launch-stage and prefixes the
// description with its tag (GA renders no tag); only private preview also
// hides the field from autocomplete.
tests := []struct {
name string
stage clijson.LaunchStage
wantDesc string
wantSuppress bool
}{
{"private preview", clijson.LaunchStagePrivatePreview, "[Private Preview] A field.", true},
{"public beta", clijson.LaunchStagePublicBeta, "[Beta] A field.", false},
{"public preview", clijson.LaunchStagePublicPreview, "[Public Preview] A field.", false},
{"GA", clijson.LaunchStageGA, "A field.", false},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
s := &jsonschema.Schema{}
assignAnnotation(s, annotation.Descriptor{Description: "A field.", LaunchStage: tc.stage})
assert.Equal(t, tc.wantDesc, s.Description)
assert.Equal(t, tc.wantSuppress, s.DoNotSuggest)
assert.Equal(t, string(tc.stage), s.LaunchStage)
})
assert.Equal(t, "[Beta] A field.", s.Description)
})
}

t.Run("private preview also hides from autocomplete", func(t *testing.T) {
t.Run("unstamped field emits no stage", func(t *testing.T) {
s := &jsonschema.Schema{}
// The private-preview stage both prefixes the description and hides the
// field; it is also emitted as x-databricks-launch-stage for pydabs.
assignAnnotation(s, annotation.Descriptor{
Description: "Internal field.",
LaunchStage: "PRIVATE_PREVIEW",
})
assert.Equal(t, "[Private Preview] Internal field.", s.Description)
assert.True(t, s.DoNotSuggest)
assert.Equal(t, "PRIVATE_PREVIEW", s.LaunchStage)
assignAnnotation(s, annotation.Descriptor{Description: "A field."})
assert.Equal(t, "A field.", s.Description)
assert.Empty(t, s.LaunchStage)
})

t.Run("per-enum-value launch stages do not leak into description", func(t *testing.T) {
Expand Down Expand Up @@ -222,7 +220,8 @@ func TestBuildEnumDescriptions(t *testing.T) {
enum := []any{"STORAGE_OPTIMIZED", "STANDARD"}

t.Run("combines launch stage and description per value", func(t *testing.T) {
got := buildEnumDescriptions(enum,
got := buildEnumDescriptions(
enum,
map[string]clijson.LaunchStage{"STORAGE_OPTIMIZED": "PUBLIC_PREVIEW"},
map[string]string{
"STORAGE_OPTIMIZED": "Storage-optimized endpoint.",
Expand All @@ -236,15 +235,17 @@ func TestBuildEnumDescriptions(t *testing.T) {
})

t.Run("launch stage only emits bracketed label", func(t *testing.T) {
got := buildEnumDescriptions(enum,
got := buildEnumDescriptions(
enum,
map[string]clijson.LaunchStage{"STORAGE_OPTIMIZED": "PUBLIC_BETA"},
nil,
)
assert.Equal(t, []string{"[Beta]", ""}, got)
})

t.Run("description only is preserved verbatim", func(t *testing.T) {
got := buildEnumDescriptions(enum,
got := buildEnumDescriptions(
enum,
nil,
map[string]string{"STORAGE_OPTIMIZED": "Storage-optimized endpoint."},
)
Expand All @@ -253,7 +254,8 @@ func TestBuildEnumDescriptions(t *testing.T) {

t.Run("returns nil when neither stage nor description has content", func(t *testing.T) {
assert.Nil(t, buildEnumDescriptions(enum, nil, nil))
assert.Nil(t, buildEnumDescriptions(enum,
assert.Nil(t, buildEnumDescriptions(
enum,
map[string]clijson.LaunchStage{"STORAGE_OPTIMIZED": "GA"},
nil,
))
Expand Down
20 changes: 13 additions & 7 deletions bundle/internal/schema/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,17 @@ func (p *annotationParser) extractAnnotations(typ reflect.Type) (annotation.File
}

basePath := getPath(typ)
// The contract carries no schema-level launch stage, so a type is
// never itself marked private-preview — only its fields are (below).
// Enum schemas do carry per-value launch stages and descriptions.
// A type carries no launch stage by default, so we set to GA, unless overridden.
typeLaunchStage := annotation.OverrideLaunchStage(basePath, "")
enumLaunchStages, enumErr := notableEnumLaunchStages(ref.EnumLaunchStages)
if enumErr != nil {
stageErr = errors.Join(stageErr, fmt.Errorf("%s: %w", basePath, enumErr))
}
enumDescriptions := nonEmptyEnumDescriptions(ref.EnumDescriptions)
if ref.Description != "" || ref.Enum != nil || enumLaunchStages != nil || enumDescriptions != nil {
if ref.Description != "" || ref.Enum != nil || enumLaunchStages != nil || enumDescriptions != nil || typeLaunchStage != "" {
annotations.SetSelf(basePath, annotation.Descriptor{
Description: ref.Description,
LaunchStage: typeLaunchStage,
Enum: enumValues(ref.Enum),
EnumLaunchStages: enumLaunchStages,
EnumDescriptions: enumDescriptions,
Expand All @@ -199,9 +199,15 @@ func (p *annotationParser) extractAnnotations(typ reflect.Type) (annotation.File

for k := range s.Properties {
if refProp, ok := ref.Fields[k]; ok {
launchStage, fieldErr := normalizeLaunchStage(refProp.LaunchStage)
if fieldErr != nil {
stageErr = errors.Join(stageErr, fmt.Errorf("%s.%s: %w", basePath, k, fieldErr))
// An empty stage means the contract assigns none; keep it
// unmarked rather than letting ParseLaunchStage default it to GA.
var launchStage clijson.LaunchStage
if refProp.LaunchStage != "" {
stage, fieldErr := clijson.ParseLaunchStage(refProp.LaunchStage)
if fieldErr != nil {
stageErr = errors.Join(stageErr, fmt.Errorf("%s.%s: %w", basePath, k, fieldErr))
}
launchStage = stage
}
// Apply custom launch stage override (e.g. keep resource in Beta despite API being GA)
launchStage = annotation.OverrideLaunchStage(basePath, launchStage)
Expand Down
16 changes: 16 additions & 0 deletions bundle/internal/schema/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ func TestExtractAnnotationsOverridesLaunchStage(t *testing.T) {
assert.Equal(t, clijson.LaunchStagePublicBeta, got.LaunchStage)
}

// TestExtractAnnotationsStampsTypeLaunchStage asserts a resource type in the
// override map carries the override stage on its own (self) descriptor, so the
// type-level x-databricks-launch-stage is emitted, not just its fields'. The
// contract carries no type-level stage, so the override map is the only source.
func TestExtractAnnotationsStampsTypeLaunchStage(t *testing.T) {
p := newParser(map[string]*clijson.SchemaJSON{
"postgres.RoleRoleSpec": {Fields: map[string]*clijson.SchemaFieldJSON{}},
})

annotations, err := p.extractAnnotations(reflect.TypeFor[resources.PostgresRole]())
require.NoError(t, err)

self := annotations[getPath(reflect.TypeFor[resources.PostgresRole]())].Self
assert.Equal(t, clijson.LaunchStagePublicBeta, self.LaunchStage)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this test break once postgres role moves of beta? Where is this value even coming from?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For your context: postgres resources in DABs are pinned to Beta despite APIs being GA (#6226)

I assume when we remove that override eventually, this test will fail and be updated/removed alongside.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

}

func TestNormalizeLaunchStage(t *testing.T) {
tests := []struct {
input string
Expand Down
Loading
Loading