Skip to content

Commit 93d7cc8

Browse files
authored
feat!: add convert functions for toplevel oneofs. (#41)
* add convert functions for toplevel oneofs. Signed-off-by: Tino Rusch <tino.rusch@gmail.com>
1 parent 0798959 commit 93d7cc8

File tree

13 files changed

+339
-4
lines changed

13 files changed

+339
-4
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.16
55
require (
66
github.com/getkin/kin-openapi v0.14.0
77
github.com/mitchellh/go-homedir v1.1.0
8+
github.com/mitchellh/mapstructure v1.4.1 // indirect
89
github.com/pkg/errors v0.8.1
910
github.com/rs/zerolog v1.20.0
1011
github.com/spf13/cobra v1.1.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
129129
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
130130
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
131131
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
132+
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
133+
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
132134
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
133135
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
134136
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=

pkg/generators/models/models.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ const (
4040
Enum ModelKind = "enum"
4141
// Value is a value type
4242
Value ModelKind = "value"
43+
// OneOf is a oneof value
44+
OneOf ModelKind = "oneof"
4345
)
4446

4547
// Model is a template model for rendering Go code for a given API schema
@@ -55,6 +57,8 @@ type Model struct {
5557
Kind ModelKind
5658
// Properties is a list of type's property descriptors
5759
Properties []PropSpec
60+
// ConvertSpecs contains a list of convert functions for this model
61+
ConvertSpecs []ConvertSpec
5862
// GoType is a string that represents the Go type that follows the model type name.
5963
// For example, `type Name GoType`.
6064
GoType string
@@ -66,6 +70,12 @@ type Model struct {
6670
PackageName string
6771
}
6872

73+
// ConvertSpec holds all info to build one As{Type}() function
74+
type ConvertSpec struct {
75+
// TargetGoType is the target type of the conversion
76+
TargetGoType string
77+
}
78+
6979
// PropSpec is a Go property descriptor
7080
type PropSpec struct {
7181
// Name is a property name in structs, variable name in enums, etc
@@ -131,6 +141,9 @@ func NewModelFromRef(ref *openapi3.SchemaRef) (model *Model, err error) {
131141
model.GoType = "map[string]" + goTypeFromSpec(ref.Value.AdditionalProperties)
132142
}
133143
}
144+
case len(ref.Value.OneOf) > 0:
145+
model.Kind = OneOf
146+
model.fillConvertSpecs(ref)
134147
default:
135148
model.Kind = Value
136149
model.GoType = goTypeFromSpec(ref)
@@ -176,6 +189,15 @@ func NewModelFromParameters(params openapi3.Parameters) (model *Model, err error
176189
return model, nil
177190
}
178191

192+
func (m *Model) fillConvertSpecs(ref *openapi3.SchemaRef) {
193+
for _, oneOf := range ref.Value.OneOf {
194+
m.ConvertSpecs = append(m.ConvertSpecs, ConvertSpec{
195+
TargetGoType: goTypeFromSpec(oneOf),
196+
})
197+
}
198+
199+
}
200+
179201
// Render renders the model to a Go file
180202
func (m *Model) Render(ctx context.Context, writer io.Writer) error {
181203
var tpl *template.Template
@@ -187,6 +209,8 @@ func (m *Model) Render(ctx context.Context, writer io.Writer) error {
187209
tpl = enumTemplate
188210
case Value:
189211
tpl = valueTemplate
212+
case OneOf:
213+
tpl = oneOfTemplate
190214
}
191215

192216
err := tpl.Execute(writer, m)

pkg/generators/models/templates.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ var (
2525
Parse(valueTemplateSource),
2626
)
2727

28+
oneOfTemplate = template.Must(
29+
template.New("oneof").
30+
Funcs(fmap).
31+
Parse(oneOfTemplateSource),
32+
)
33+
2834
fmap = template.FuncMap{
2935
"firstLower": tpl.FirstLower,
3036
"firstUpper": tpl.FirstUpper,
@@ -150,6 +156,50 @@ var (
150156
{{- end}}
151157
)
152158
)
159+
`
160+
161+
oneOfTemplateSource = `
162+
// This file is auto-generated, DO NOT EDIT.
163+
//
164+
// Source:
165+
// Title: {{.SpecTitle}}
166+
// Version: {{.SpecVersion}}
167+
package {{ .PackageName }}
168+
169+
import (
170+
"encoding/json"
171+
"github.com/mitchellh/mapstructure"
172+
)
173+
174+
{{ (printf "%s is a oneOf type. %s" .Name .Description) | commentBlock }}
175+
type {{.Name}} struct {
176+
data interface{}
177+
}
178+
179+
{{- $modelName := .Name }}
180+
181+
// MarshalJSON implementes the json.Marshaller interface
182+
func (m *{{$modelName}}) MarshalJSON() ([]byte, error) {
183+
return json.Marshal(m.data)
184+
}
185+
186+
// UnmarshalJSON implementes the json.Unmarshaller interface
187+
func (m *{{$modelName}}) UnmarshalJSON(bs []byte) error {
188+
return json.Unmarshal(bs, &m.data)
189+
}
190+
191+
// As converts {{$modelName}} to a user defined structure.
192+
func (m {{$modelName}}) As(target interface{}) error {
193+
return mapstructure.Decode(m.data, target)
194+
}
195+
196+
{{- range $convert := .ConvertSpecs }}
197+
// As{{firstUpper $convert.TargetGoType}} converts {{$modelName}} to a {{$convert.TargetGoType}}
198+
func (m {{$modelName}}) As{{firstUpper $convert.TargetGoType}}() (result {{$convert.TargetGoType}}, err error) {
199+
return result, mapstructure.Decode(m.data, &result)
200+
}
201+
202+
{{- end}}
153203
`
154204

155205
valueTemplateSource = `

pkg/generators/models/testdata/cases/oneof/api.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,16 @@ components:
1717
- type: string
1818
- type: integer
1919

20+
Baz:
21+
oneOf:
22+
- $ref: "#/components/schemas/Foo"
23+
- $ref: "#/components/schemas/Bar"
24+
- $ref: "#/components/schemas/Person"
25+
26+
Person:
27+
type: object
28+
properties:
29+
name:
30+
type: string
31+
age:
32+
type: integer

pkg/generators/models/testdata/cases/oneof/expected/model_bar.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,37 @@
55
// Version: 0.1.0
66
package generatortest
77

8-
// Bar is a value type.
9-
type Bar interface{}
8+
import (
9+
"encoding/json"
10+
"github.com/mitchellh/mapstructure"
11+
)
12+
13+
// Bar is a oneOf type.
14+
type Bar struct {
15+
data interface{}
16+
}
17+
18+
// MarshalJSON implementes the json.Marshaller interface
19+
func (m *Bar) MarshalJSON() ([]byte, error) {
20+
return json.Marshal(m.data)
21+
}
22+
23+
// UnmarshalJSON implementes the json.Unmarshaller interface
24+
func (m *Bar) UnmarshalJSON(bs []byte) error {
25+
return json.Unmarshal(bs, &m.data)
26+
}
27+
28+
// As converts Bar to a user defined structure.
29+
func (m Bar) As(target interface{}) error {
30+
return mapstructure.Decode(m.data, target)
31+
}
32+
33+
// AsString converts Bar to a string
34+
func (m Bar) AsString() (result string, err error) {
35+
return result, mapstructure.Decode(m.data, &result)
36+
}
37+
38+
// AsInt32 converts Bar to a int32
39+
func (m Bar) AsInt32() (result int32, err error) {
40+
return result, mapstructure.Decode(m.data, &result)
41+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// This file is auto-generated, DO NOT EDIT.
2+
//
3+
// Source:
4+
// Title: Test
5+
// Version: 0.1.0
6+
package generatortest
7+
8+
import (
9+
"encoding/json"
10+
"github.com/mitchellh/mapstructure"
11+
)
12+
13+
// Baz is a oneOf type.
14+
type Baz struct {
15+
data interface{}
16+
}
17+
18+
// MarshalJSON implementes the json.Marshaller interface
19+
func (m *Baz) MarshalJSON() ([]byte, error) {
20+
return json.Marshal(m.data)
21+
}
22+
23+
// UnmarshalJSON implementes the json.Unmarshaller interface
24+
func (m *Baz) UnmarshalJSON(bs []byte) error {
25+
return json.Unmarshal(bs, &m.data)
26+
}
27+
28+
// As converts Baz to a user defined structure.
29+
func (m Baz) As(target interface{}) error {
30+
return mapstructure.Decode(m.data, target)
31+
}
32+
33+
// AsFoo converts Baz to a Foo
34+
func (m Baz) AsFoo() (result Foo, err error) {
35+
return result, mapstructure.Decode(m.data, &result)
36+
}
37+
38+
// AsBar converts Baz to a Bar
39+
func (m Baz) AsBar() (result Bar, err error) {
40+
return result, mapstructure.Decode(m.data, &result)
41+
}
42+
43+
// AsPerson converts Baz to a Person
44+
func (m Baz) AsPerson() (result Person, err error) {
45+
return result, mapstructure.Decode(m.data, &result)
46+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// This file is auto-generated, DO NOT EDIT.
2+
//
3+
// Source:
4+
// Title: Test
5+
// Version: 0.1.0
6+
package generatortest
7+
8+
import (
9+
validation "github.com/go-ozzo/ozzo-validation/v4"
10+
)
11+
12+
// Person is an object.
13+
type Person struct {
14+
// Age:
15+
Age int32 `json:"age,omitempty"`
16+
// Name:
17+
Name string `json:"name,omitempty"`
18+
}
19+
20+
// Validate implements basic validation for this model
21+
func (m Person) Validate() error {
22+
return validation.Errors{}.Filter()
23+
}
24+
25+
// GetAge returns the Age property
26+
func (m Person) GetAge() int32 {
27+
return m.Age
28+
}
29+
30+
// SetAge sets the Age property
31+
func (m *Person) SetAge(val int32) {
32+
m.Age = val
33+
}
34+
35+
// GetName returns the Name property
36+
func (m Person) GetName() string {
37+
return m.Name
38+
}
39+
40+
// SetName sets the Name property
41+
func (m *Person) SetName(val string) {
42+
m.Name = val
43+
}

pkg/generators/models/testdata/cases/oneof/generated/model_bar.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,37 @@
55
// Version: 0.1.0
66
package generatortest
77

8-
// Bar is a value type.
9-
type Bar interface{}
8+
import (
9+
"encoding/json"
10+
"github.com/mitchellh/mapstructure"
11+
)
12+
13+
// Bar is a oneOf type.
14+
type Bar struct {
15+
data interface{}
16+
}
17+
18+
// MarshalJSON implementes the json.Marshaller interface
19+
func (m *Bar) MarshalJSON() ([]byte, error) {
20+
return json.Marshal(m.data)
21+
}
22+
23+
// UnmarshalJSON implementes the json.Unmarshaller interface
24+
func (m *Bar) UnmarshalJSON(bs []byte) error {
25+
return json.Unmarshal(bs, &m.data)
26+
}
27+
28+
// As converts Bar to a user defined structure.
29+
func (m Bar) As(target interface{}) error {
30+
return mapstructure.Decode(m.data, target)
31+
}
32+
33+
// AsString converts Bar to a string
34+
func (m Bar) AsString() (result string, err error) {
35+
return result, mapstructure.Decode(m.data, &result)
36+
}
37+
38+
// AsInt32 converts Bar to a int32
39+
func (m Bar) AsInt32() (result int32, err error) {
40+
return result, mapstructure.Decode(m.data, &result)
41+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// This file is auto-generated, DO NOT EDIT.
2+
//
3+
// Source:
4+
// Title: Test
5+
// Version: 0.1.0
6+
package generatortest
7+
8+
import (
9+
"encoding/json"
10+
"github.com/mitchellh/mapstructure"
11+
)
12+
13+
// Baz is a oneOf type.
14+
type Baz struct {
15+
data interface{}
16+
}
17+
18+
// MarshalJSON implementes the json.Marshaller interface
19+
func (m *Baz) MarshalJSON() ([]byte, error) {
20+
return json.Marshal(m.data)
21+
}
22+
23+
// UnmarshalJSON implementes the json.Unmarshaller interface
24+
func (m *Baz) UnmarshalJSON(bs []byte) error {
25+
return json.Unmarshal(bs, &m.data)
26+
}
27+
28+
// As converts Baz to a user defined structure.
29+
func (m Baz) As(target interface{}) error {
30+
return mapstructure.Decode(m.data, target)
31+
}
32+
33+
// AsFoo converts Baz to a Foo
34+
func (m Baz) AsFoo() (result Foo, err error) {
35+
return result, mapstructure.Decode(m.data, &result)
36+
}
37+
38+
// AsBar converts Baz to a Bar
39+
func (m Baz) AsBar() (result Bar, err error) {
40+
return result, mapstructure.Decode(m.data, &result)
41+
}
42+
43+
// AsPerson converts Baz to a Person
44+
func (m Baz) AsPerson() (result Person, err error) {
45+
return result, mapstructure.Decode(m.data, &result)
46+
}

0 commit comments

Comments
 (0)