-
Notifications
You must be signed in to change notification settings - Fork 4
/
schema_util.go
145 lines (125 loc) · 3.31 KB
/
schema_util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package jsonschema
import (
"strings"
"github.com/caddyserver/caddy/v2"
)
func generateSchema() error {
// fetch all caddy modules available in current build
for _, mod := range caddy.Modules() {
split := strings.Split(mod, ".")
parent := "" // top level modules
name := split[len(split)-1]
if len(split) >= 2 {
// submodules
parent = strings.Join(split[:len(split)-1], ".")
}
info, err := caddy.GetModule(mod)
if err != nil {
return err
}
if moduleMap[parent] == nil {
moduleMap[parent] = Modules{}
}
newInfo := info.New()
module := Module{
Name: name,
Type: newInfo,
Interface: Interface{
Name: name,
Module: mod,
},
}
moduleMap[parent][name] = module
flatModuleMap[mod] = module
}
// schema generation
// use separate loop to ensure module list has populated.
{
// all module definitions
definitions := map[string]*Schema{}
for modName, module := range flatModuleMap {
module.Interface.populate(module.Type)
schema := module.Interface.toSchema()
if doc, ok := flatCaddyDocMap[modName]; ok {
addDocToSchema(schema, doc.Result.Structure)
}
definitions[modName] = schema
}
// full config
configField := Interface{}
configField.populate(caddy.Config{})
rootSchema = configField.toSchema()
rootSchema.Definitions = definitions
// in case this schema is incomplete, support additional custom items.
rootSchema.AdditionalItems = true
// docs
rootSchema.Title = "Caddy v2 autogenerated JSON schema \nhttps://github.com/abiosoft/caddy-json-schema"
rootSchema.Type = "object"
if rootDocAPIResp.Result.Structure != nil {
addDocToSchema(rootSchema, rootDocAPIResp.Result.Structure)
}
}
return nil
}
func addDocToSchema(s *Schema, doc *DocStruct) {
if s == nil || doc == nil {
return
}
desc := func(description, pkg, doc string) string {
pkg = godocLink(pkg)
desc := ""
for _, d := range []string{description, pkg, doc} {
if d != "" {
desc += d + "\n"
}
}
return desc
}
mdDesc := func(description, pkg, doc string) string {
pkg = markdownLink("godoc", godocLink(pkg))
desc := ""
for _, d := range []string{description, pkg, doc} {
if strings.TrimSpace(d) != "" {
desc += d + " \n"
}
}
return desc
}
setDesc := func(s *Schema, d *DocStruct) {
s.Description = desc(s.description, d.Package, d.Doc)
s.MarkdownDescription = mdDesc(s.markdownDescription, d.Package, d.Doc)
}
// set only if non-empty, parent may have set the doc if empty
if doc.Doc != "" {
setDesc(s, doc)
}
switch doc.Type {
case "struct":
for i, field := range doc.StructFields {
if _, ok := s.Properties[field.Key]; ok {
// if field has no doc, use parent doc.
if field.Value.Doc == "" {
setDesc(s.Properties[field.Key], field)
}
addDocToSchema(s.Properties[field.Key], doc.StructFields[i].Value)
}
}
case "array":
if s.ArrayItems != nil && doc.Elems != nil {
if doc.Elems.Doc != "" {
setDesc(s, doc.Elems) // use items doc for parent
}
addDocToSchema(s.ArrayItems, doc.Elems)
}
case "map":
if s.AdditionalProperties != nil && doc.Elems != nil {
if doc.Elems.Doc != "" {
setDesc(s, doc.Elems) // use items doc for parent
}
addDocToSchema(s.AdditionalProperties, doc.Elems)
}
default:
// everything else has no nesting
// do nothing/terminate
}
}