Skip to content

Commit d0b235e

Browse files
nikiccwI2L
andauthored
feat: support providing examples for pointer fields (#56)
Co-authored-by: William Poussier <[email protected]>
1 parent 6c2edb3 commit d0b235e

File tree

4 files changed

+230
-39
lines changed

4 files changed

+230
-39
lines changed

openapi/generator.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,11 +1235,39 @@ func parseExampleValue(t reflect.Type, value string) (interface{}, error) {
12351235
case reflect.String:
12361236
return value, nil
12371237
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1238-
return strconv.ParseInt(value, 10, t.Bits())
1238+
i, err := strconv.ParseInt(value, 10, t.Bits())
1239+
if err != nil {
1240+
return nil, err
1241+
}
1242+
switch t.Bits() {
1243+
case 8:
1244+
return int8(i), nil
1245+
case 16:
1246+
return int16(i), nil
1247+
case 32:
1248+
return int32(i), nil
1249+
default:
1250+
}
1251+
return i, nil
12391252
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
1240-
return strconv.ParseUint(value, 10, t.Bits())
1253+
u, err := strconv.ParseUint(value, 10, t.Bits())
1254+
if err != nil {
1255+
return nil, err
1256+
}
1257+
switch t.Bits() {
1258+
case 8:
1259+
return uint8(u), nil
1260+
case 16:
1261+
return uint16(u), nil
1262+
case 32:
1263+
return uint32(u), nil
1264+
default:
1265+
}
1266+
return u, nil
12411267
case reflect.Float32, reflect.Float64:
12421268
return strconv.ParseFloat(value, t.Bits())
1269+
case reflect.Ptr:
1270+
return parseExampleValue(t.Elem(), value)
12431271
default:
12441272
return nil, fmt.Errorf("unsuported type: %s", t.String())
12451273
}

openapi/generator_test.go

Lines changed: 194 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package openapi
33
import (
44
"encoding/json"
55
"io/ioutil"
6+
"math"
67
"reflect"
78
"testing"
89
"time"
@@ -129,6 +130,20 @@ func TestSchemaFromPrimitiveType(t *testing.T) {
129130
assert.True(t, schema.Nullable)
130131
}
131132

133+
// TestSchemaFromInterface tests that a schema
134+
// can be created for an interface{} value that
135+
// represent *any* type.
136+
func TestSchemaFromInterface(t *testing.T) {
137+
g := gen(t)
138+
139+
schema := g.newSchemaFromType(tofEmptyInterface)
140+
assert.NotNil(t, schema)
141+
assert.Empty(t, schema.Type)
142+
assert.Empty(t, schema.Format)
143+
assert.True(t, schema.Nullable)
144+
assert.NotEmpty(t, schema.Description)
145+
}
146+
132147
// TestSchemaFromUnsupportedType tests that a schema
133148
// cannot be created given an unsupported input type.
134149
func TestSchemaFromUnsupportedType(t *testing.T) {
@@ -232,10 +247,16 @@ func TestNewSchemaFromStructFieldExampleValues(t *testing.T) {
232247
g := gen(t)
233248

234249
type T struct {
235-
A string `example:"value"`
236-
B int `example:"1"`
237-
C float64 `example:"0.1"`
238-
D bool `example:"true"`
250+
A string `example:"value"`
251+
APtr *string `example:"value"`
252+
B int `example:"1"`
253+
BPtr *int `example:"1"`
254+
C float64 `example:"0.1"`
255+
CPtr *float64 `example:"0.1"`
256+
D bool `example:"true"`
257+
DPtr *bool `example:"true"`
258+
EPtr **bool `example:"false"`
259+
FPtr ***uint16 `example:"128"`
239260
}
240261
typ := reflect.TypeOf(T{})
241262

@@ -244,20 +265,50 @@ func TestNewSchemaFromStructFieldExampleValues(t *testing.T) {
244265
assert.NotNil(t, sor)
245266
assert.Equal(t, "value", sor.Example)
246267

268+
// Field APtr contains pointer to string example.
269+
sor = g.newSchemaFromStructField(typ.Field(1), false, "APtr", typ)
270+
assert.NotNil(t, sor)
271+
assert.Equal(t, "value", sor.Example)
272+
247273
// Field B contains int example.
248-
sor = g.newSchemaFromStructField(typ.Field(1), false, "B", typ)
274+
sor = g.newSchemaFromStructField(typ.Field(2), false, "B", typ)
275+
assert.NotNil(t, sor)
276+
assert.Equal(t, int64(1), sor.Example)
277+
278+
// Field BPtr contains pointer to int example.
279+
sor = g.newSchemaFromStructField(typ.Field(3), false, "BPtr", typ)
249280
assert.NotNil(t, sor)
250281
assert.Equal(t, int64(1), sor.Example)
251282

252283
// Field C contains float example.
253-
sor = g.newSchemaFromStructField(typ.Field(2), false, "C", typ)
284+
sor = g.newSchemaFromStructField(typ.Field(4), false, "C", typ)
285+
assert.NotNil(t, sor)
286+
assert.Equal(t, 0.1, sor.Example)
287+
288+
// Field CPtr contains pointer to float example.
289+
sor = g.newSchemaFromStructField(typ.Field(5), false, "CPtr", typ)
254290
assert.NotNil(t, sor)
255291
assert.Equal(t, 0.1, sor.Example)
256292

257293
// Field D contains boolean example.
258-
sor = g.newSchemaFromStructField(typ.Field(3), false, "D", typ)
294+
sor = g.newSchemaFromStructField(typ.Field(6), false, "D", typ)
295+
assert.NotNil(t, sor)
296+
assert.Equal(t, true, sor.Example)
297+
298+
// Field DPtr contains pointer to boolean example.
299+
sor = g.newSchemaFromStructField(typ.Field(7), false, "DPtr", typ)
259300
assert.NotNil(t, sor)
260301
assert.Equal(t, true, sor.Example)
302+
303+
// Field EPtr contains a double-pointer to boolean example.
304+
sor = g.newSchemaFromStructField(typ.Field(8), false, "EPtr", typ)
305+
assert.NotNil(t, sor)
306+
assert.Equal(t, false, sor.Example)
307+
308+
// Field FPtr contains a triple-pointer to uint16 value example.
309+
sor = g.newSchemaFromStructField(typ.Field(9), false, "FPtr", typ)
310+
assert.NotNil(t, sor)
311+
assert.Equal(t, uint16(128), sor.Example)
261312
}
262313

263314
// TestNewSchemaFromStructFieldErrors tests the errors
@@ -365,23 +416,23 @@ func TestAddOperation(t *testing.T) {
365416
Description: "XYZ",
366417
Deprecated: true,
367418
Responses: []*OperationResponse{
368-
&OperationResponse{
419+
{
369420
Code: "400",
370421
Description: "Bad Request",
371422
Model: CustomError{},
372423
},
373-
&OperationResponse{
424+
{
374425
Code: "5XX",
375426
Description: "Server Errors",
376427
},
377428
},
378429
Headers: []*ResponseHeader{
379-
&ResponseHeader{
430+
{
380431
Name: "X-Test-Header",
381432
Description: "Test header",
382433
Model: Header,
383434
},
384-
&ResponseHeader{
435+
{
385436
Name: "X-Test-Header-Alt",
386437
Description: "Test header alt",
387438
},
@@ -659,16 +710,22 @@ func TestSetServers(t *testing.T) {
659710
g := gen(t)
660711

661712
servers := []*Server{
662-
&Server{URL: "https://dev.api.foo.bar/v1", Description: "Development server"},
663-
&Server{URL: "https://prod.api.foo.bar/{basePath}", Description: "Production server", Variables: map[string]*ServerVariable{
664-
"basePath": &ServerVariable{
665-
Description: "Version of the API",
666-
Enum: []string{
667-
"v1", "v2", "beta",
713+
{
714+
URL: "https://dev.api.foo.bar/v1",
715+
Description: "Development server",
716+
},
717+
{
718+
URL: "https://prod.api.foo.bar/{basePath}",
719+
Description: "Production server",
720+
Variables: map[string]*ServerVariable{
721+
"basePath": {
722+
Description: "Version of the API",
723+
Enum: []string{
724+
"v1", "v2", "beta",
725+
},
726+
Default: "v2",
668727
},
669-
Default: "v2",
670-
},
671-
}},
728+
}},
672729
}
673730
g.SetServers(servers)
674731

@@ -689,27 +746,133 @@ func TestGenerator_parseExampleValue(t *testing.T) {
689746
reflect.TypeOf("value"),
690747
"value",
691748
"value",
692-
}, {
693-
"mapping to int",
694-
reflect.TypeOf(1),
695-
"1",
696-
int64(1),
697-
}, {
749+
},
750+
{
751+
"mapping pointer to string",
752+
reflect.PtrTo(reflect.TypeOf("value")),
753+
"value",
754+
"value",
755+
},
756+
{
757+
"mapping to int8",
758+
reflect.TypeOf(int8(math.MaxInt8)),
759+
"127",
760+
int8(math.MaxInt8),
761+
},
762+
{
763+
"mapping pointer to int8",
764+
reflect.PtrTo(reflect.TypeOf(int8(math.MaxInt8))),
765+
"127",
766+
int8(math.MaxInt8),
767+
},
768+
{
769+
"mapping to int16",
770+
reflect.TypeOf(int16(math.MaxInt16)),
771+
"32767",
772+
int16(math.MaxInt16),
773+
},
774+
{
775+
"mapping pointer to int16",
776+
reflect.PtrTo(reflect.TypeOf(int16(math.MaxInt16))),
777+
"32767",
778+
int16(math.MaxInt16),
779+
},
780+
{
781+
"mapping to int32",
782+
reflect.TypeOf(int32(math.MaxInt32)),
783+
"2147483647",
784+
int32(math.MaxInt32),
785+
},
786+
{
787+
"mapping pointer to int32",
788+
reflect.PtrTo(reflect.TypeOf(int32(math.MaxInt32))),
789+
"2147483647",
790+
int32(math.MaxInt32),
791+
},
792+
{
793+
"mapping to int64",
794+
reflect.TypeOf(int64(math.MaxInt64)),
795+
"9223372036854775807",
796+
int64(math.MaxInt64),
797+
},
798+
{
799+
"mapping pointer to int64",
800+
reflect.PtrTo(reflect.TypeOf(int64(math.MaxInt64))),
801+
"9223372036854775807",
802+
int64(math.MaxInt64),
803+
},
804+
{
698805
"mapping to uint8",
699-
reflect.TypeOf(uint8(1)),
700-
"1",
701-
uint64(1),
702-
}, {
806+
reflect.TypeOf(uint8(math.MaxUint8)),
807+
"255",
808+
uint8(math.MaxUint8),
809+
},
810+
{
811+
"mapping pointer to uint8",
812+
reflect.PtrTo(reflect.TypeOf(uint8(math.MaxUint8))),
813+
"255",
814+
uint8(math.MaxUint8),
815+
},
816+
{
817+
"mapping to uint16",
818+
reflect.TypeOf(uint16(math.MaxUint16)),
819+
"65535",
820+
uint16(math.MaxUint16),
821+
},
822+
{
823+
"mapping pointer to uint16",
824+
reflect.PtrTo(reflect.TypeOf(uint16(math.MaxUint16))),
825+
"65535",
826+
uint16(math.MaxUint16),
827+
},
828+
{
829+
"mapping to uint32",
830+
reflect.TypeOf(uint32(math.MaxUint32)),
831+
"4294967295",
832+
uint32(math.MaxUint32),
833+
},
834+
{
835+
"mapping pointer to uint32",
836+
reflect.PtrTo(reflect.TypeOf(uint32(math.MaxUint32))),
837+
"4294967295",
838+
uint32(math.MaxUint32),
839+
},
840+
{
841+
"mapping to uint64",
842+
reflect.TypeOf(uint64(math.MaxUint64)),
843+
"18446744073709551615",
844+
uint64(math.MaxUint64),
845+
},
846+
{
847+
"mapping pointer to uint64",
848+
reflect.PtrTo(reflect.TypeOf(uint64(math.MaxUint64))),
849+
"18446744073709551615",
850+
uint64(math.MaxUint64),
851+
},
852+
{
703853
"mapping to number",
704854
reflect.TypeOf(1.23),
705855
"1.23",
706856
1.23,
707-
}, {
857+
},
858+
{
859+
"mapping pointer to number",
860+
reflect.PtrTo(reflect.TypeOf(1.23)),
861+
"1.23",
862+
1.23,
863+
},
864+
{
708865
"mapping to boolean",
709866
reflect.TypeOf(true),
710867
"true",
711868
true,
712869
},
870+
{
871+
"mapping pointer to boolean",
872+
reflect.PtrTo(reflect.TypeOf(true)),
873+
"true",
874+
true,
875+
},
713876
}
714877

715878
for _, tc := range testCases {

openapi/sort_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import (
1111
// sort functions.
1212
func TestSortParams(t *testing.T) {
1313
params := []*ParameterOrRef{
14-
&ParameterOrRef{Parameter: &Parameter{Name: "Paz", In: "path"}},
15-
&ParameterOrRef{Parameter: &Parameter{Name: "Baz", In: "header"}},
16-
&ParameterOrRef{Parameter: &Parameter{Name: "Bar", In: "query"}},
17-
&ParameterOrRef{Parameter: &Parameter{Name: "Zap", In: "path"}},
18-
&ParameterOrRef{Parameter: &Parameter{Name: "Foo", In: "query"}},
14+
{Parameter: &Parameter{Name: "Paz", In: "path"}},
15+
{Parameter: &Parameter{Name: "Baz", In: "header"}},
16+
{Parameter: &Parameter{Name: "Bar", In: "query"}},
17+
{Parameter: &Parameter{Name: "Zap", In: "path"}},
18+
{Parameter: &Parameter{Name: "Foo", In: "query"}},
1919
}
2020
// Sort by location, then by name in ascending order.
2121
paramsOrderedBy(byLocation, byName).Sort(params)

openapi/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func DataTypeFromType(t reflect.Type) DataType {
122122
}
123123
// Dereference any pointer.
124124
if t.Kind() == reflect.Ptr {
125-
t = t.Elem()
125+
return DataTypeFromType(t.Elem())
126126
}
127127
// Switch over Golang types.
128128
switch t {

0 commit comments

Comments
 (0)