2024-12-04 21:50:34 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-12-06 14:07:05 +00:00
|
|
|
"bytes"
|
2024-12-04 21:50:34 +00:00
|
|
|
"os"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/ghodss/yaml"
|
2024-12-10 12:44:20 +00:00
|
|
|
yaml3 "gopkg.in/yaml.v3"
|
2024-12-04 21:50:34 +00:00
|
|
|
|
2024-12-06 14:07:05 +00:00
|
|
|
"github.com/databricks/cli/libs/dyn"
|
|
|
|
"github.com/databricks/cli/libs/dyn/convert"
|
|
|
|
"github.com/databricks/cli/libs/dyn/merge"
|
|
|
|
"github.com/databricks/cli/libs/dyn/yamlloader"
|
2024-12-10 11:54:50 +00:00
|
|
|
"github.com/databricks/cli/libs/dyn/yamlsaver"
|
2024-12-04 21:50:34 +00:00
|
|
|
"github.com/databricks/cli/libs/jsonschema"
|
|
|
|
)
|
|
|
|
|
|
|
|
type annotation struct {
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
MarkdownDescription string `json:"markdown_description,omitempty"`
|
|
|
|
Title string `json:"title,omitempty"`
|
|
|
|
Default any `json:"default,omitempty"`
|
2024-12-06 14:07:05 +00:00
|
|
|
Enum []any `json:"enum,omitempty"`
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type annotationHandler struct {
|
2024-12-10 11:54:50 +00:00
|
|
|
ref annotationFile
|
|
|
|
empty annotationFile
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
|
|
|
|
2024-12-10 11:54:50 +00:00
|
|
|
type annotationFile map[string]map[string]annotation
|
|
|
|
|
2024-12-04 21:50:34 +00:00
|
|
|
const Placeholder = "PLACEHOLDER"
|
|
|
|
|
2024-12-06 14:07:05 +00:00
|
|
|
// Adds annotations to the JSON schema reading from the annotation files.
|
2024-12-04 21:50:34 +00:00
|
|
|
// More details https://json-schema.org/understanding-json-schema/reference/annotations
|
2024-12-06 14:07:05 +00:00
|
|
|
func newAnnotationHandler(sources []string) (*annotationHandler, error) {
|
|
|
|
prev := dyn.NilValue
|
|
|
|
for _, path := range sources {
|
|
|
|
b, err := os.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
generated, err := yamlloader.LoadYAML(path, bytes.NewBuffer(b))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
prev, err = merge.Merge(prev, generated)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
|
|
|
|
2024-12-10 11:54:50 +00:00
|
|
|
var data annotationFile
|
2024-12-06 14:07:05 +00:00
|
|
|
|
|
|
|
err := convert.ToTyped(&data, prev)
|
2024-12-04 21:50:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
d := &annotationHandler{}
|
|
|
|
d.ref = data
|
2024-12-10 11:54:50 +00:00
|
|
|
d.empty = annotationFile{}
|
2024-12-04 21:50:34 +00:00
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *annotationHandler) addAnnotations(typ reflect.Type, s jsonschema.Schema) jsonschema.Schema {
|
2024-12-09 15:23:02 +00:00
|
|
|
refPath := getPath(typ)
|
2024-12-10 11:54:50 +00:00
|
|
|
shouldHandle := strings.HasPrefix(refPath, "github.com")
|
|
|
|
if !shouldHandle {
|
|
|
|
return s
|
|
|
|
}
|
2024-12-04 21:50:34 +00:00
|
|
|
|
2024-12-10 11:54:50 +00:00
|
|
|
annotations := d.ref[refPath]
|
|
|
|
if annotations == nil {
|
|
|
|
annotations = map[string]annotation{}
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
|
|
|
|
2024-12-10 11:54:50 +00:00
|
|
|
rootTypeAnnotation, ok := annotations[RootTypeKey]
|
|
|
|
if ok {
|
|
|
|
assingAnnotation(&s, rootTypeAnnotation)
|
|
|
|
}
|
2024-12-04 21:50:34 +00:00
|
|
|
|
2024-12-10 11:54:50 +00:00
|
|
|
for k, v := range s.Properties {
|
|
|
|
item, ok := annotations[k]
|
2024-12-04 21:50:34 +00:00
|
|
|
if !ok {
|
2024-12-06 14:07:05 +00:00
|
|
|
item = annotation{}
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
2024-12-06 14:07:05 +00:00
|
|
|
if item.Description == "" {
|
|
|
|
item.Description = Placeholder
|
2024-12-04 21:50:34 +00:00
|
|
|
|
2024-12-10 11:54:50 +00:00
|
|
|
emptyAnnotations := d.empty[refPath]
|
|
|
|
if emptyAnnotations == nil {
|
|
|
|
emptyAnnotations = map[string]annotation{}
|
|
|
|
d.empty[refPath] = emptyAnnotations
|
|
|
|
}
|
|
|
|
emptyAnnotations[k] = item
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
2024-12-10 11:54:50 +00:00
|
|
|
assingAnnotation(v, item)
|
2024-12-04 21:50:34 +00:00
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2024-12-06 14:07:05 +00:00
|
|
|
// Adds empty annotations with placeholders to the annotation file
|
|
|
|
func (d *annotationHandler) sync(outputPath string) error {
|
2024-12-10 12:44:20 +00:00
|
|
|
existingFile, err := os.ReadFile(outputPath)
|
2024-12-04 21:50:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-12-10 13:00:00 +00:00
|
|
|
missingAnnotations, err := yaml.Marshal(d.empty)
|
2024-12-10 11:54:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2024-12-06 14:07:05 +00:00
|
|
|
}
|
2024-12-10 13:00:00 +00:00
|
|
|
err = saveYamlWithStyle(outputPath, existingFile, missingAnnotations)
|
2024-12-04 21:50:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2024-12-09 15:23:02 +00:00
|
|
|
|
|
|
|
func getPath(typ reflect.Type) string {
|
|
|
|
return typ.PkgPath() + "." + typ.Name()
|
|
|
|
}
|
2024-12-10 11:54:50 +00:00
|
|
|
|
|
|
|
func assingAnnotation(s *jsonschema.Schema, a annotation) {
|
|
|
|
if a.Description != Placeholder {
|
|
|
|
s.Description = a.Description
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.Default != nil {
|
|
|
|
s.Default = a.Default
|
|
|
|
}
|
|
|
|
s.MarkdownDescription = a.MarkdownDescription
|
|
|
|
s.Title = a.Title
|
|
|
|
s.Enum = a.Enum
|
|
|
|
}
|
2024-12-10 13:00:00 +00:00
|
|
|
|
|
|
|
func saveYamlWithStyle(outputPath string, input []byte, overrides []byte) error {
|
|
|
|
inputDyn, err := yamlloader.LoadYAML("", bytes.NewBuffer(input))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(overrides) != 0 {
|
|
|
|
overrideDyn, err := yamlloader.LoadYAML("", bytes.NewBuffer(overrides))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
inputDyn, err = merge.Merge(inputDyn, overrideDyn)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
style := map[string]yaml3.Style{}
|
|
|
|
file, _ := inputDyn.AsMap()
|
|
|
|
for _, v := range file.Keys() {
|
|
|
|
style[v.MustString()] = yaml3.LiteralStyle
|
|
|
|
}
|
|
|
|
|
|
|
|
saver := yamlsaver.NewSaverWithStyle(style)
|
|
|
|
err = saver.SaveAsYAML(file, outputPath, true)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|