Skip to content

Commit c9362ea

Browse files
committed
Enhance example-from-schema generator.
Fixes #24, #25. Adds support for enums, formats, enhances support for array and object examples, etc.
1 parent 3bb5b77 commit c9362ea

File tree

3 files changed

+517
-221
lines changed

3 files changed

+517
-221
lines changed

apisprout.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func getTypedExample(mt *openapi3.MediaType) (interface{}, error) {
152152
}
153153

154154
if mt.Schema != nil {
155-
return getTypedExampleFromSchema(mt.Schema.Value)
155+
return OpenAPIExample(mt.Schema.Value)
156156
}
157157
// TODO: generate data from JSON schema, if no examples available?
158158

example.go

Lines changed: 130 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,161 @@ import (
66
"github.com/getkin/kin-openapi/openapi3"
77
)
88

9-
// getTypedExampleFromSchema will return an example from a given schema
10-
func getTypedExampleFromSchema(schema *openapi3.Schema) (interface{}, error) {
9+
func getSchemaExample(schema *openapi3.Schema) (interface{}, bool) {
1110
if schema.Example != nil {
12-
return schema.Example, nil
11+
return schema.Example, true
1312
}
1413

15-
if schema.Type == "number" {
16-
return 0, nil
14+
if schema.Default != nil {
15+
return schema.Default, true
1716
}
18-
if schema.Type == "integer" {
19-
return 0, nil
17+
18+
if schema.Enum != nil && len(schema.Enum) > 0 {
19+
return schema.Enum[0], true
2020
}
21-
if schema.Type == "boolean" {
22-
return true, nil
21+
22+
return nil, false
23+
}
24+
25+
// stringFormatExample returns an example string based on the given format.
26+
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3
27+
func stringFormatExample(format string) string {
28+
switch format {
29+
case "date":
30+
// https://tools.ietf.org/html/rfc3339
31+
return "2018-07-23"
32+
case "date-time":
33+
// This is the date/time of API Sprout's first commit! :-)
34+
return "2018-07-23T22:58:00-07:00"
35+
case "time":
36+
return "22:58:00-07:00"
37+
case "email":
38+
39+
case "hostname":
40+
// https://tools.ietf.org/html/rfc2606#page-2
41+
return "example.com"
42+
case "ipv4":
43+
// https://tools.ietf.org/html/rfc5737
44+
return "198.51.100.0"
45+
case "ipv6":
46+
// https://tools.ietf.org/html/rfc3849
47+
return "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
48+
case "uri":
49+
return "https://tools.ietf.org/html/rfc3986"
50+
case "uri-template":
51+
// https://tools.ietf.org/html/rfc6570
52+
return "http://example.com/dictionary/{term:1}/{term}"
53+
case "json-pointer":
54+
// https://tools.ietf.org/html/rfc6901
55+
return "#/components/parameters/term"
56+
case "regex":
57+
// https://stackoverflow.com/q/3296050/164268
58+
return "/^1?$|^(11+?)\\1+$/"
59+
case "uuid":
60+
// https://www.ietf.org/rfc/rfc4122.txt
61+
return "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
62+
case "password":
63+
return "********"
2364
}
24-
if schema.Type == "string" {
25-
return "string", nil
65+
66+
return ""
67+
}
68+
69+
// OpenAPIExample creates an example structure from an OpenAPI 3 schema
70+
// object, which is an extended subset of JSON Schema.
71+
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#schemaObject
72+
func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
73+
if ex, ok := getSchemaExample(schema); ok {
74+
return ex, nil
2675
}
27-
if schema.Type == "array" {
76+
77+
switch {
78+
case schema.Type == "boolean":
79+
return true, nil
80+
case schema.Type == "number", schema.Type == "integer":
81+
value := 0.0
82+
83+
if schema.Min != nil && *schema.Min > value {
84+
value = *schema.Min
85+
if schema.ExclusiveMin {
86+
if schema.Max != nil {
87+
// Make the value half way.
88+
value = (*schema.Min + *schema.Max) / 2.0
89+
} else {
90+
value++
91+
}
92+
}
93+
}
94+
95+
if schema.Max != nil && *schema.Max < value {
96+
value = *schema.Max
97+
if schema.ExclusiveMax {
98+
if schema.Min != nil {
99+
// Make the value half way.
100+
value = (*schema.Min + *schema.Max) / 2.0
101+
} else {
102+
value--
103+
}
104+
}
105+
}
106+
107+
if schema.MultipleOf != nil && int(value)%int(*schema.MultipleOf) != 0 {
108+
value += float64(int(*schema.MultipleOf) - (int(value) % int(*schema.MultipleOf)))
109+
}
110+
111+
if schema.Type == "integer" {
112+
return int(value), nil
113+
}
114+
115+
return value, nil
116+
case schema.Type == "string":
117+
if ex := stringFormatExample(schema.Format); ex != "" {
118+
return ex, nil
119+
}
120+
121+
example := "string"
122+
123+
for schema.MinLength > uint64(len(example)) {
124+
example += example
125+
}
126+
127+
if schema.MaxLength != nil && *schema.MaxLength < uint64(len(example)) {
128+
example = example[:*schema.MaxLength]
129+
}
130+
131+
return example, nil
132+
case schema.Type == "array", schema.Items != nil:
28133
example := []interface{}{}
134+
29135
if schema.Items != nil && schema.Items.Value != nil {
30-
ex, err := getTypedExampleFromSchema(schema.Items.Value)
136+
ex, err := OpenAPIExample(schema.Items.Value)
31137
if err != nil {
32138
return nil, fmt.Errorf("can't get example for array item")
33139
}
140+
34141
example = append(example, ex)
142+
143+
for uint64(len(example)) < schema.MinItems {
144+
example = append(example, ex)
145+
}
35146
}
36-
return example, nil
37-
}
38147

39-
if schema.Type == "object" || len(schema.Properties) > 0 {
148+
return example, nil
149+
case schema.Type == "object", len(schema.Properties) > 0:
40150
example := map[string]interface{}{}
151+
41152
for k, v := range schema.Properties {
42-
ex, err := getTypedExampleFromSchema(v.Value)
153+
ex, err := OpenAPIExample(v.Value)
43154
if err != nil {
44155
return nil, fmt.Errorf("can't get example for '%s'", k)
45156
}
157+
46158
example[k] = ex
47159
}
48160

49161
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Value != nil {
50162
addl := schema.AdditionalProperties.Value
51-
ex, err := getTypedExampleFromSchema(addl)
163+
ex, err := OpenAPIExample(addl)
52164
if err != nil {
53165
return nil, fmt.Errorf("can't get example for additional properties")
54166
}

0 commit comments

Comments
 (0)