package schema import ( _ "embed" "encoding/json" "fmt" "os" "reflect" "github.com/databricks/bricks/bundle/config" "github.com/databricks/databricks-sdk-go/openapi" ) // A subset of Schema struct type Docs struct { Description string `json:"description"` Properties map[string]*Docs `json:"properties,omitempty"` Items *Docs `json:"items,omitempty"` AdditionalProperties *Docs `json:"additionalproperties,omitempty"` } //go:embed docs/bundle_descriptions.json var bundleDocs []byte func BundleDocs(openapiSpecPath string) (*Docs, error) { docs, err := initializeBundleDocs() if err != nil { return nil, err } if openapiSpecPath != "" { openapiSpec, err := os.ReadFile(openapiSpecPath) if err != nil { return nil, err } spec := &openapi.Specification{} err = json.Unmarshal(openapiSpec, spec) if err != nil { return nil, err } openapiReader := &OpenapiReader{ OpenapiSpec: spec, Memo: make(map[string]*Schema), } resourcesDocs, err := openapiReader.ResourcesDocs() if err != nil { return nil, err } resourceSchema, err := New(reflect.TypeOf(config.Resources{}), resourcesDocs) if err != nil { return nil, err } docs.Properties["resources"] = schemaToDocs(resourceSchema) } docs.refreshEnvironmentsDocs() return docs, nil } func (docs *Docs) refreshEnvironmentsDocs() error { environmentsDocs, ok := docs.Properties["environments"] if !ok || environmentsDocs.AdditionalProperties == nil || environmentsDocs.AdditionalProperties.Properties == nil { return fmt.Errorf("invalid environments descriptions") } environmentProperties := environmentsDocs.AdditionalProperties.Properties propertiesToCopy := []string{"artifacts", "bundle", "resources", "workspace"} for _, p := range propertiesToCopy { environmentProperties[p] = docs.Properties[p] } return nil } func initializeBundleDocs() (*Docs, error) { // load embedded descriptions embedded := Docs{} err := json.Unmarshal(bundleDocs, &embedded) if err != nil { return nil, err } // generate schema with the embedded descriptions schema, err := New(reflect.TypeOf(config.Root{}), &embedded) if err != nil { return nil, err } // converting the schema back to docs. This creates empty descriptions // for any properties that were missing in the embedded descriptions docs := schemaToDocs(schema) return docs, nil } // *Docs are a subset of *Schema, this function selects that subset func schemaToDocs(schema *Schema) *Docs { // terminate recursion if schema is nil if schema == nil { return nil } docs := &Docs{ Description: schema.Description, } if len(schema.Properties) > 0 { docs.Properties = make(map[string]*Docs) } for k, v := range schema.Properties { docs.Properties[k] = schemaToDocs(v) } docs.Items = schemaToDocs(schema.Items) if additionalProperties, ok := schema.AdditionalProperties.(*Schema); ok { docs.AdditionalProperties = schemaToDocs(additionalProperties) } return docs }