2023-01-20 15:55:44 +00:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
2023-01-23 14:00:11 +00:00
|
|
|
_ "embed"
|
2023-03-15 02:18:51 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2023-01-20 15:55:44 +00:00
|
|
|
"os"
|
2023-03-15 02:18:51 +00:00
|
|
|
"reflect"
|
2023-01-20 15:55:44 +00:00
|
|
|
|
2023-05-16 16:35:39 +00:00
|
|
|
"github.com/databricks/cli/bundle/config"
|
2023-08-01 14:09:27 +00:00
|
|
|
"github.com/databricks/cli/libs/jsonschema"
|
2023-03-15 02:18:51 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/openapi"
|
2023-01-20 15:55:44 +00:00
|
|
|
)
|
|
|
|
|
2023-03-15 02:18:51 +00:00
|
|
|
// A subset of Schema struct
|
2023-01-20 15:55:44 +00:00
|
|
|
type Docs struct {
|
2023-03-15 02:18:51 +00:00
|
|
|
Description string `json:"description"`
|
|
|
|
Properties map[string]*Docs `json:"properties,omitempty"`
|
|
|
|
Items *Docs `json:"items,omitempty"`
|
|
|
|
AdditionalProperties *Docs `json:"additionalproperties,omitempty"`
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
|
2023-03-15 02:18:51 +00:00
|
|
|
//go:embed docs/bundle_descriptions.json
|
|
|
|
var bundleDocs []byte
|
|
|
|
|
2023-08-17 15:22:32 +00:00
|
|
|
func (docs *Docs) refreshTargetsDocs() error {
|
|
|
|
targetsDocs, ok := docs.Properties["targets"]
|
|
|
|
if !ok || targetsDocs.AdditionalProperties == nil ||
|
|
|
|
targetsDocs.AdditionalProperties.Properties == nil {
|
|
|
|
return fmt.Errorf("invalid targets descriptions")
|
2023-03-15 02:18:51 +00:00
|
|
|
}
|
2023-08-17 15:22:32 +00:00
|
|
|
targetProperties := targetsDocs.AdditionalProperties.Properties
|
2023-03-15 02:18:51 +00:00
|
|
|
propertiesToCopy := []string{"artifacts", "bundle", "resources", "workspace"}
|
|
|
|
for _, p := range propertiesToCopy {
|
2023-08-17 15:22:32 +00:00
|
|
|
targetProperties[p] = docs.Properties[p]
|
2023-03-15 02:18:51 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-01-23 14:00:11 +00:00
|
|
|
|
2023-12-06 10:45:18 +00:00
|
|
|
func LoadBundleDescriptions() (*Docs, error) {
|
2023-03-15 02:18:51 +00:00
|
|
|
embedded := Docs{}
|
|
|
|
err := json.Unmarshal(bundleDocs, &embedded)
|
2023-12-06 10:45:18 +00:00
|
|
|
return &embedded, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func UpdateBundleDescriptions(openapiSpecPath string) (*Docs, error) {
|
|
|
|
embedded, err := LoadBundleDescriptions()
|
2023-03-15 02:18:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-12-06 10:45:18 +00:00
|
|
|
|
|
|
|
// Generate schema from the embedded descriptions, and convert it back to docs.
|
|
|
|
// This creates empty descriptions for any properties that were missing in the
|
|
|
|
// embedded descriptions.
|
|
|
|
schema, err := New(reflect.TypeOf(config.Root{}), embedded)
|
2023-01-23 14:00:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-03-15 02:18:51 +00:00
|
|
|
docs := schemaToDocs(schema)
|
2023-12-06 10:45:18 +00:00
|
|
|
|
|
|
|
// Load the Databricks OpenAPI spec
|
|
|
|
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,
|
2024-05-01 11:04:37 +00:00
|
|
|
memo: make(map[string]jsonschema.Schema),
|
2023-12-06 10:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate descriptions for the "resources" field
|
|
|
|
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.refreshTargetsDocs()
|
2023-03-15 02:18:51 +00:00
|
|
|
return docs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// *Docs are a subset of *Schema, this function selects that subset
|
2023-08-01 14:09:27 +00:00
|
|
|
func schemaToDocs(jsonSchema *jsonschema.Schema) *Docs {
|
2023-03-15 02:18:51 +00:00
|
|
|
// terminate recursion if schema is nil
|
2023-08-01 14:09:27 +00:00
|
|
|
if jsonSchema == nil {
|
2023-03-15 02:18:51 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
docs := &Docs{
|
2023-08-01 14:09:27 +00:00
|
|
|
Description: jsonSchema.Description,
|
2023-03-15 02:18:51 +00:00
|
|
|
}
|
2023-08-01 14:09:27 +00:00
|
|
|
if len(jsonSchema.Properties) > 0 {
|
2023-03-15 02:18:51 +00:00
|
|
|
docs.Properties = make(map[string]*Docs)
|
|
|
|
}
|
2023-08-01 14:09:27 +00:00
|
|
|
for k, v := range jsonSchema.Properties {
|
2023-03-15 02:18:51 +00:00
|
|
|
docs.Properties[k] = schemaToDocs(v)
|
|
|
|
}
|
2023-08-01 14:09:27 +00:00
|
|
|
docs.Items = schemaToDocs(jsonSchema.Items)
|
|
|
|
if additionalProperties, ok := jsonSchema.AdditionalProperties.(*jsonschema.Schema); ok {
|
2023-03-15 02:18:51 +00:00
|
|
|
docs.AdditionalProperties = schemaToDocs(additionalProperties)
|
|
|
|
}
|
|
|
|
return docs
|
2023-01-23 14:00:11 +00:00
|
|
|
}
|