diff --git a/entoas/generator.go b/entoas/generator.go index 0a2d6f8ee..6c6c048dc 100644 --- a/entoas/generator.go +++ b/entoas/generator.go @@ -534,27 +534,49 @@ var ( max16 int64 = math.MaxInt16 maxu16 int64 = math.MaxUint16 maxu32 int64 = math.MaxUint32 - types = map[string]*ogen.Schema{ - "bool": ogen.Bool(), - "time.Time": ogen.DateTime(), - "string": ogen.String(), - "[]byte": ogen.Bytes(), - "uuid.UUID": ogen.UUID(), - "int": ogen.Int(), - "int8": ogen.Int32().SetMinimum(&min8).SetMaximum(&max8), - "int16": ogen.Int32().SetMinimum(&min16).SetMaximum(&max16), - "int32": ogen.Int32(), - "uint": ogen.Int64().SetMinimum(&zero).SetMaximum(&maxu32), - "uint8": ogen.Int32().SetMinimum(&zero).SetMaximum(&maxu8), - "uint16": ogen.Int32().SetMinimum(&zero).SetMaximum(&maxu16), - "uint32": ogen.Int64().SetMinimum(&zero).SetMaximum(&maxu32), - "int64": ogen.Int64(), - "uint64": ogen.Int64().SetMinimum(&zero), - "float32": ogen.Float(), - "float64": ogen.Double(), - } ) +func types(t string) *ogen.Schema { + switch t { + case "bool": + return ogen.Bool() + case "time.Time": + return ogen.DateTime() + case "string": + return ogen.String() + case "[]byte": + return ogen.Bytes() + case "uuid.UUID": + return ogen.UUID() + case "int": + return ogen.Int() + case "int8": + return ogen.Int32().SetMinimum(&min8).SetMaximum(&max8) + case "int16": + return ogen.Int32().SetMinimum(&min16).SetMaximum(&max16) + case "int32": + return ogen.Int32() + case "uint": + return ogen.Int64().SetMinimum(&zero).SetMaximum(&maxu32) + case "uint8": + return ogen.Int32().SetMinimum(&zero).SetMaximum(&maxu8) + case "uint16": + return ogen.Int32().SetMinimum(&zero).SetMaximum(&maxu16) + case "uint32": + return ogen.Int64().SetMinimum(&zero).SetMaximum(&maxu32) + case "int64": + return ogen.Int64() + case "uint64": + return ogen.Int64().SetMinimum(&zero) + case "float32": + return ogen.Float() + case "float64": + return ogen.Double() + default: + return nil + } +} + // OgenSchema returns the ogen.Schema to use for the given gen.Field. func OgenSchema(f *gen.Field) (*ogen.Schema, error) { // If there is a custom property given on the field use it. @@ -565,8 +587,12 @@ func OgenSchema(f *gen.Field) (*ogen.Schema, error) { if ant.Schema != nil { return ant.Schema, nil } - // Enum values need special case. - if f.IsEnum() { + + var schema *ogen.Schema + s := f.Type.String() + + switch { + case f.IsEnum(): // Enum values need special case. var d json.RawMessage if f.Default { d, err = json.Marshal(f.DefaultValue().(string)) @@ -581,20 +607,33 @@ func OgenSchema(f *gen.Field) (*ogen.Schema, error) { return nil, err } } - return ogen.String().AsEnum(d, vs...), nil - } - s := f.Type.String() - // Handle slice types. - if strings.HasPrefix(s, "[]") { - if t, ok := types[s[2:]]; ok { - return t.AsArray(), nil + schema = ogen.String().AsEnum(d, vs...) + + case strings.HasPrefix(s, "[]"): // Handle slice types. + t := types(s[2:]) + if t != nil { + schema = t.AsArray() + break } + fallthrough + + default: + schema = types(s) } - t, ok := types[s] - if !ok { + + if schema == nil { return nil, fmt.Errorf("no OAS-type exists for type %q of field %s", s, f.StructField()) } - return t, nil + + if ant.Example != nil { + jv, err := json.Marshal(ant.Example) + if err != nil { + return nil, fmt.Errorf("cannot marshal example annotation for field %s", f.Name) + } + schema.Example = jv + } + + return schema, nil } // NodeOperations returns the list of operations to expose for this node. diff --git a/entoas/generator_test.go b/entoas/generator_test.go index 9d9425a20..bc8a8d244 100644 --- a/entoas/generator_test.go +++ b/entoas/generator_test.go @@ -102,6 +102,47 @@ func TestOgenSchema(t *testing.T) { } } +func TestOgenSchema_Example(t *testing.T) { + t.Parallel() + entFields := map[*entfield.Descriptor]*ogen.Schema{ + entfield.String("name"). + Annotations(Example("name")).Descriptor(): func() *ogen.Schema { + schema := ogen.String() + v, err := json.Marshal("name") + require.NoError(t, err) + schema.Example = v + return schema + }(), + entfield.Float32("total"). + Annotations(Example("total")).Descriptor(): func() *ogen.Schema { + schema := ogen.Float() + v, err := json.Marshal("total") + require.NoError(t, err) + schema.Example = v + return schema + }(), + } + + for d, ex := range entFields { + t.Run(d.Name, func(t *testing.T) { + f, err := load.NewField(d) + require.NoError(t, err) + gf := &gen.Field{ + Name: f.Name, + Type: f.Info, + Annotations: f.Annotations, + } + ant, err := FieldAnnotation(gf) + require.NoError(t, err) + require.Equal(t, ant.Example, gf.Name) + + ac, err := OgenSchema(gf) + require.NoError(t, err) + require.Equal(t, ex, ac) + }) + } +} + func TestOperation_Title(t *testing.T) { t.Parallel() require.Equal(t, "Create", OpCreate.Title()) diff --git a/entoas/internal/pets/openapi.json b/entoas/internal/pets/openapi.json index 953547a2f..6f982f02a 100644 --- a/entoas/internal/pets/openapi.json +++ b/entoas/internal/pets/openapi.json @@ -400,7 +400,8 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -409,7 +410,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "categories": { "type": "array", @@ -564,7 +566,8 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -573,7 +576,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "categories": { "type": "array", @@ -1215,7 +1219,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1224,7 +1229,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 } }, "required": [ @@ -1239,7 +1245,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1248,7 +1255,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "categories": { "type": "array", @@ -1278,7 +1286,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1287,7 +1296,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 } }, "required": [ @@ -1302,7 +1312,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1311,7 +1322,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 } }, "required": [ @@ -1323,7 +1335,8 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1332,7 +1345,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "owner": { "$ref": "#/components/schemas/PetRead_Owner" @@ -1368,7 +1382,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1377,7 +1392,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 } }, "required": [ @@ -1407,7 +1423,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1416,7 +1433,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 } }, "required": [ @@ -1551,7 +1569,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1560,7 +1579,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 } }, "required": [ diff --git a/entoas/internal/simple/openapi.json b/entoas/internal/simple/openapi.json index 639e2f068..d2e0b359e 100644 --- a/entoas/internal/simple/openapi.json +++ b/entoas/internal/simple/openapi.json @@ -400,7 +400,8 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -409,7 +410,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "categories": { "type": "array", @@ -564,7 +566,8 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -573,7 +576,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "categories": { "type": "array", @@ -1159,7 +1163,8 @@ "type": "integer" }, "name": { - "type": "string" + "type": "string", + "example": "Kuro" }, "nicknames": { "type": "array", @@ -1168,7 +1173,8 @@ } }, "age": { - "type": "integer" + "type": "integer", + "example": 1 }, "categories": { "type": "array",