2024-12-18 16:07:09 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/databricks/cli/libs/jsonschema"
|
|
|
|
)
|
|
|
|
|
|
|
|
type rootNode struct {
|
|
|
|
Title string
|
|
|
|
Description string
|
|
|
|
Attributes []attributeNode
|
|
|
|
Example string
|
|
|
|
ObjectKeyAttributes []attributeNode
|
|
|
|
ArrayItemAttributes []attributeNode
|
|
|
|
TopLevel bool
|
2025-01-03 12:52:59 +00:00
|
|
|
Type string
|
2024-12-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type attributeNode struct {
|
|
|
|
Title string
|
|
|
|
Type string
|
|
|
|
Description string
|
2025-01-10 11:52:20 +00:00
|
|
|
Reference string
|
2024-12-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type rootProp struct {
|
|
|
|
k string
|
|
|
|
v *jsonschema.Schema
|
|
|
|
topLevel bool
|
|
|
|
}
|
|
|
|
|
2025-01-10 11:52:20 +00:00
|
|
|
const MapType = "Map"
|
|
|
|
|
2024-12-18 19:53:38 +00:00
|
|
|
func getNodes(s jsonschema.Schema, refs map[string]jsonschema.Schema, customFields map[string]bool) []rootNode {
|
2024-12-18 16:07:09 +00:00
|
|
|
rootProps := []rootProp{}
|
|
|
|
for k, v := range s.Properties {
|
|
|
|
rootProps = append(rootProps, rootProp{k, v, true})
|
|
|
|
}
|
|
|
|
nodes := make([]rootNode, 0, len(rootProps))
|
2025-01-10 11:52:20 +00:00
|
|
|
visited := make(map[string]bool)
|
2024-12-18 16:07:09 +00:00
|
|
|
|
|
|
|
for i := 0; i < len(rootProps); i++ {
|
2024-12-18 19:53:38 +00:00
|
|
|
item := rootProps[i]
|
|
|
|
k := item.k
|
|
|
|
v := item.v
|
2025-01-10 11:52:20 +00:00
|
|
|
|
|
|
|
if visited[k] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
visited[k] = true
|
2024-12-18 16:07:09 +00:00
|
|
|
v = resolveRefs(v, refs)
|
|
|
|
node := rootNode{
|
|
|
|
Title: k,
|
2024-12-18 19:53:38 +00:00
|
|
|
Description: getDescription(v, item.topLevel),
|
|
|
|
TopLevel: item.topLevel,
|
2025-01-02 15:13:24 +00:00
|
|
|
Example: getExample(v),
|
2025-01-03 12:52:59 +00:00
|
|
|
Type: getHumanReadableType(v.Type),
|
2024-12-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
2025-01-10 18:26:45 +00:00
|
|
|
node.Attributes = getAttributes(v.Properties, refs, customFields, k)
|
2024-12-18 19:53:38 +00:00
|
|
|
rootProps = append(rootProps, extractNodes(k, v.Properties, refs, customFields)...)
|
2024-12-18 16:07:09 +00:00
|
|
|
|
|
|
|
additionalProps, ok := v.AdditionalProperties.(*jsonschema.Schema)
|
|
|
|
if ok {
|
|
|
|
objectKeyType := resolveRefs(additionalProps, refs)
|
2025-01-10 11:52:20 +00:00
|
|
|
d := getDescription(objectKeyType, true)
|
|
|
|
if d != "" {
|
|
|
|
node.Description = d
|
2025-01-08 15:11:35 +00:00
|
|
|
}
|
|
|
|
if len(node.Example) == 0 {
|
|
|
|
node.Example = getExample(objectKeyType)
|
|
|
|
}
|
2025-01-13 14:16:47 +00:00
|
|
|
prefix := k + ".<name>"
|
2025-01-10 20:01:15 +00:00
|
|
|
node.ObjectKeyAttributes = getAttributes(objectKeyType.Properties, refs, customFields, prefix)
|
|
|
|
rootProps = append(rootProps, extractNodes(prefix, objectKeyType.Properties, refs, customFields)...)
|
2024-12-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if v.Items != nil {
|
|
|
|
arrayItemType := resolveRefs(v.Items, refs)
|
2025-01-10 18:26:45 +00:00
|
|
|
node.ArrayItemAttributes = getAttributes(arrayItemType.Properties, refs, customFields, k)
|
2024-12-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
2025-01-10 18:57:02 +00:00
|
|
|
isEmpty := node.Description == "" && len(node.Attributes) == 0 && len(node.ObjectKeyAttributes) == 0 && len(node.ArrayItemAttributes) == 0
|
2024-12-18 18:59:08 +00:00
|
|
|
shouldAddNode := !isEmpty || node.TopLevel
|
|
|
|
if shouldAddNode {
|
|
|
|
nodes = append(nodes, node)
|
|
|
|
}
|
2024-12-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(nodes, func(i, j int) bool {
|
|
|
|
return nodes[i].Title < nodes[j].Title
|
|
|
|
})
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
|
2025-01-03 12:52:59 +00:00
|
|
|
func removePluralForm(s string) string {
|
|
|
|
if strings.HasSuffix(s, "s") {
|
|
|
|
return strings.TrimSuffix(s, "s")
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func getHumanReadableType(t jsonschema.Type) string {
|
2024-12-18 16:07:09 +00:00
|
|
|
typesMapping := map[string]string{
|
|
|
|
"string": "String",
|
|
|
|
"integer": "Integer",
|
|
|
|
"boolean": "Boolean",
|
|
|
|
"array": "Sequence",
|
|
|
|
"object": "Map",
|
|
|
|
}
|
2025-01-03 12:52:59 +00:00
|
|
|
return typesMapping[string(t)]
|
|
|
|
}
|
2024-12-18 16:07:09 +00:00
|
|
|
|
2025-01-10 18:26:45 +00:00
|
|
|
func getAttributes(props map[string]*jsonschema.Schema, refs map[string]jsonschema.Schema, customFields map[string]bool, prefix string) []attributeNode {
|
2024-12-18 16:07:09 +00:00
|
|
|
attributes := []attributeNode{}
|
|
|
|
for k, v := range props {
|
|
|
|
v = resolveRefs(v, refs)
|
2025-01-03 12:52:59 +00:00
|
|
|
typeString := getHumanReadableType(v.Type)
|
2024-12-18 16:07:09 +00:00
|
|
|
if typeString == "" {
|
|
|
|
typeString = "Any"
|
|
|
|
}
|
2025-01-10 11:52:20 +00:00
|
|
|
var reference string
|
2025-01-10 18:26:45 +00:00
|
|
|
if isReferenceType(v, refs, customFields) {
|
2025-01-10 11:52:20 +00:00
|
|
|
reference = prefix + "." + k
|
|
|
|
}
|
2024-12-18 16:07:09 +00:00
|
|
|
attributes = append(attributes, attributeNode{
|
|
|
|
Title: k,
|
|
|
|
Type: typeString,
|
2024-12-18 19:53:38 +00:00
|
|
|
Description: getDescription(v, true),
|
2025-01-10 11:52:20 +00:00
|
|
|
Reference: reference,
|
2024-12-18 16:07:09 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
sort.Slice(attributes, func(i, j int) bool {
|
|
|
|
return attributes[i].Title < attributes[j].Title
|
|
|
|
})
|
|
|
|
return attributes
|
|
|
|
}
|
|
|
|
|
2024-12-18 19:53:38 +00:00
|
|
|
func getDescription(s *jsonschema.Schema, allowMarkdown bool) string {
|
|
|
|
if allowMarkdown && s.MarkdownDescription != "" {
|
2024-12-18 16:07:09 +00:00
|
|
|
return s.MarkdownDescription
|
|
|
|
}
|
|
|
|
return s.Description
|
|
|
|
}
|
|
|
|
|
2024-12-18 19:53:38 +00:00
|
|
|
func shouldExtract(ref string, customFields map[string]bool) bool {
|
2025-01-10 11:52:20 +00:00
|
|
|
if i := strings.Index(ref, "github.com"); i >= 0 {
|
|
|
|
ref = ref[i:]
|
|
|
|
}
|
|
|
|
_, isCustomField := customFields[ref]
|
2024-12-18 19:53:38 +00:00
|
|
|
return isCustomField
|
|
|
|
}
|
|
|
|
|
|
|
|
func extractNodes(prefix string, props map[string]*jsonschema.Schema, refs map[string]jsonschema.Schema, customFields map[string]bool) []rootProp {
|
2024-12-18 16:07:09 +00:00
|
|
|
nodes := []rootProp{}
|
|
|
|
for k, v := range props {
|
2024-12-18 19:53:38 +00:00
|
|
|
if !shouldExtract(*v.Reference, customFields) {
|
|
|
|
continue
|
|
|
|
}
|
2024-12-18 16:07:09 +00:00
|
|
|
v = resolveRefs(v, refs)
|
2025-01-10 11:52:20 +00:00
|
|
|
if v.Type == "object" || v.Type == "array" {
|
2024-12-18 16:07:09 +00:00
|
|
|
nodes = append(nodes, rootProp{prefix + "." + k, v, false})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nodes
|
|
|
|
}
|
2025-01-02 15:13:24 +00:00
|
|
|
|
|
|
|
func getExample(v *jsonschema.Schema) string {
|
|
|
|
examples := v.Examples
|
|
|
|
if len(examples) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return examples[0].(string)
|
|
|
|
}
|