-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support providing examples for custom types #73
Conversation
7080bcb
to
ba321bc
Compare
Codecov Report
@@ Coverage Diff @@
## master #73 +/- ##
==========================================
- Coverage 95.19% 95.01% -0.19%
==========================================
Files 7 7
Lines 957 962 +5
==========================================
+ Hits 911 914 +3
- Misses 30 32 +2
Partials 16 16
Continue to review full report at Codecov.
|
openapi/generator.go
Outdated
@@ -1235,6 +1235,10 @@ func fieldNameFromTag(sf reflect.StructField, tagName string) string { | |||
return name | |||
} | |||
|
|||
type OpenAPIExample interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thought that this interface might be expanded in the future for others features. Perhaps we should take that opportunity to rename it CustomType
, or something else (if you have a better idead, please share) and aggregate all methods within it.
Also, exported type needs a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed; CustomType
seems more general and easier to use for other stuff later if needed.
Added a comment & renamed to CustomType
openapi/generator.go
Outdated
@@ -1276,6 +1280,13 @@ func parseExampleValue(t reflect.Type, value string) (interface{}, error) { | |||
return strconv.ParseFloat(value, t.Bits()) | |||
case reflect.Ptr: | |||
return parseExampleValue(t.Elem(), value) | |||
case reflect.Struct: | |||
// we only support structs that implement OpenAPIExample interface | |||
casted, ok := reflect.New(t).Interface().(OpenAPIExample) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/casted/i
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
README.md
Outdated
To be able to provide examples for structs, they must implement the following interface: | ||
```go | ||
type OpenAPIExample interface { | ||
ParseExample(v string) interface{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method should be able to return an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Added an error now.
openapi/generator.go
Outdated
// we only support structs that implement OpenAPIExample interface | ||
casted, ok := reflect.New(t).Interface().(OpenAPIExample) | ||
if ok { | ||
return casted.ParseExample(value), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return i.ParseExample(value)
We should return the error returned by the implem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
openapi/generator.go
Outdated
if ok { | ||
return casted.ParseExample(value), nil | ||
} | ||
return nil, fmt.Errorf(" structs (type %s) that do not implement OpenAPIExample are not supported", t.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest: "type %s doesn not implement {interface name}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
openapi/generator_test.go
Outdated
@@ -733,6 +734,13 @@ func TestSetServers(t *testing.T) { | |||
assert.Equal(t, servers, g.API().Servers) | |||
} | |||
|
|||
type CustomUnit struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a test for a known case, such as your time.Time
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to. Added.
openapi/generator_test.go
Outdated
@@ -733,6 +734,13 @@ func TestSetServers(t *testing.T) { | |||
assert.Equal(t, servers, g.API().Servers) | |||
} | |||
|
|||
type CustomUnit struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't need to be exported: s/CustomUnit/customUnit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed for both the existing type and the one I just aded for time.Time
.
Thanks for the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wI2L pushed the fixes. Ready for another pass 👀
README.md
Outdated
To be able to provide examples for structs, they must implement the following interface: | ||
```go | ||
type OpenAPIExample interface { | ||
ParseExample(v string) interface{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Added an error now.
openapi/generator.go
Outdated
@@ -1235,6 +1235,10 @@ func fieldNameFromTag(sf reflect.StructField, tagName string) string { | |||
return name | |||
} | |||
|
|||
type OpenAPIExample interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed; CustomType
seems more general and easier to use for other stuff later if needed.
Added a comment & renamed to CustomType
openapi/generator.go
Outdated
@@ -1276,6 +1280,13 @@ func parseExampleValue(t reflect.Type, value string) (interface{}, error) { | |||
return strconv.ParseFloat(value, t.Bits()) | |||
case reflect.Ptr: | |||
return parseExampleValue(t.Elem(), value) | |||
case reflect.Struct: | |||
// we only support structs that implement OpenAPIExample interface | |||
casted, ok := reflect.New(t).Interface().(OpenAPIExample) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
openapi/generator.go
Outdated
// we only support structs that implement OpenAPIExample interface | ||
casted, ok := reflect.New(t).Interface().(OpenAPIExample) | ||
if ok { | ||
return casted.ParseExample(value), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
openapi/generator.go
Outdated
if ok { | ||
return casted.ParseExample(value), nil | ||
} | ||
return nil, fmt.Errorf(" structs (type %s) that do not implement OpenAPIExample are not supported", t.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
openapi/generator_test.go
Outdated
@@ -733,6 +734,13 @@ func TestSetServers(t *testing.T) { | |||
assert.Equal(t, servers, g.API().Servers) | |||
} | |||
|
|||
type CustomUnit struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to. Added.
openapi/generator_test.go
Outdated
@@ -733,6 +734,13 @@ func TestSetServers(t *testing.T) { | |||
assert.Equal(t, servers, g.API().Servers) | |||
} | |||
|
|||
type CustomUnit struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed for both the existing type and the one I just aded for time.Time
.
openapi/generator.go
Outdated
type OpenAPIExample interface { | ||
ParseExample(v string) interface{} | ||
// CustomType describes the interface of custom types that can be used in JSON responses. | ||
type CustomType interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move it to fizz/openapi/types.go
, with the other types-related interfaces?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think I'm reconsidering my proposal to name that interface CustomType
.
We already have two interfaces Type
and DataType
that are implemented by custom types.
Ideally, this should be one single interface CustomType
. Perhaps for a v1.0.
In the meantime, I think we should keep a seperate interface, Exampler
(the name sucks a bit, but we usually end interface name with "er". If you have a better name in mind, don't hesitate).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to fizz/openapi/types.go
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the meantime, I think we should keep a seperate interface, Exampler (the name sucks a bit, but we usually end interface name with "er".
Renamed to Exampler
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, this should be one single interface CustomType. Perhaps for a v1.0.
One argument for keeping these separate interfaces separate might be that it's easier to fulfil the interfaces if they are separate (e.g. if I only want to provide examples, currently I only need to implement the ParseExample
method, while with one bigger interface I'd need to implement all the methods).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you have a valid point.
openapi/generator_test.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
return t1.Format("2006-01-02T15:04:05"), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, for the sake of the example, the return should be t1
, and the type should implement a String()
and/or MarshalJson
methods. The goal is to return the concrete type, and leave the marshaling to the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not sure. If we refactor, then we would need something like this:
type customTime struct {
time.Time
}
func (c customTime) String() string {
return c.Format("2006-01-02T15:04:05")
}
func (c customTime) ParseExample(v string) (interface{}, error) {
t1, err := time.Parse(time.RFC3339, v)
if err != nil {
return nil, err
}
return customTime{t1}, nil
}
but then we can't test is as nicely in TestGenerator_parseExampleValue
as the returned value is an object, not a string representation formatted in the the format we like. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what I thought of in term of implem.
The marshaling/string representation of the custom type can be tested by comparing the output spec, and if we want to add a test of the type-specific ParseExample
method, we just need to ensure that the returned value has the proper type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored now.
openapi/generator_test.go
Outdated
"15", | ||
"15 USD", | ||
}, | ||
{ | ||
"mapping pointers to custom structs", | ||
reflect.PtrTo(reflect.TypeOf(CustomUnit{})), | ||
"mapping pointers to customUnit", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/pointers/pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
openapi/generator_test.go
Outdated
"2022-02-07T18:00:00", | ||
}, | ||
{ | ||
"mapping pointers to customTime", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/pointers/pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
openapi/generator_test.go
Outdated
@@ -882,17 +892,29 @@ func TestGenerator_parseExampleValue(t *testing.T) { | |||
true, | |||
}, | |||
{ | |||
"mapping custom structs", | |||
reflect.TypeOf(CustomUnit{}), | |||
"mapping customUnit", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/mapping/mapping to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed.
openapi/generator_test.go
Outdated
"20.00", | ||
"20.00 USD", | ||
}, | ||
{ | ||
"mapping customTime", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/mapping/mapping to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
README.md
Outdated
@@ -231,8 +231,8 @@ You can use additional tags. Some will be interpreted by *tonic*, others will be | |||
### Providing Examples for Structs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move it under the https://github.com/wI2L/fizz#openapi-specification section, wwhile keeping an anchor link from the "Additional tags" table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wI2L pushed some more fixes. Let me know what you think.
I think I addressed everything, but the test about the example for time. I think in the current format it's easier to tests so might be in favour of keeping what we have.
README.md
Outdated
@@ -231,8 +231,8 @@ You can use additional tags. Some will be interpreted by *tonic*, others will be | |||
### Providing Examples for Structs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved.
openapi/generator.go
Outdated
type OpenAPIExample interface { | ||
ParseExample(v string) interface{} | ||
// CustomType describes the interface of custom types that can be used in JSON responses. | ||
type CustomType interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to fizz/openapi/types.go
.
openapi/generator.go
Outdated
type OpenAPIExample interface { | ||
ParseExample(v string) interface{} | ||
// CustomType describes the interface of custom types that can be used in JSON responses. | ||
type CustomType interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the meantime, I think we should keep a seperate interface, Exampler (the name sucks a bit, but we usually end interface name with "er".
Renamed to Exampler
.
openapi/generator.go
Outdated
type OpenAPIExample interface { | ||
ParseExample(v string) interface{} | ||
// CustomType describes the interface of custom types that can be used in JSON responses. | ||
type CustomType interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, this should be one single interface CustomType. Perhaps for a v1.0.
One argument for keeping these separate interfaces separate might be that it's easier to fulfil the interfaces if they are separate (e.g. if I only want to provide examples, currently I only need to implement the ParseExample
method, while with one bigger interface I'd need to implement all the methods).
openapi/generator_test.go
Outdated
@@ -882,17 +892,29 @@ func TestGenerator_parseExampleValue(t *testing.T) { | |||
true, | |||
}, | |||
{ | |||
"mapping custom structs", | |||
reflect.TypeOf(CustomUnit{}), | |||
"mapping customUnit", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed.
openapi/generator_test.go
Outdated
"15", | ||
"15 USD", | ||
}, | ||
{ | ||
"mapping pointers to custom structs", | ||
reflect.PtrTo(reflect.TypeOf(CustomUnit{})), | ||
"mapping pointers to customUnit", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
openapi/generator_test.go
Outdated
"20.00", | ||
"20.00 USD", | ||
}, | ||
{ | ||
"mapping customTime", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
openapi/generator_test.go
Outdated
"2022-02-07T18:00:00", | ||
}, | ||
{ | ||
"mapping pointers to customTime", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
openapi/generator_test.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
return t1.Format("2006-01-02T15:04:05"), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not sure. If we refactor, then we would need something like this:
type customTime struct {
time.Time
}
func (c customTime) String() string {
return c.Format("2006-01-02T15:04:05")
}
func (c customTime) ParseExample(v string) (interface{}, error) {
t1, err := time.Parse(time.RFC3339, v)
if err != nil {
return nil, err
}
return customTime{t1}, nil
}
but then we can't test is as nicely in TestGenerator_parseExampleValue
as the returned value is an object, not a string representation formatted in the the format we like. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wI2L pushed the updated version of a test. WDYT?
openapi/generator_test.go
Outdated
@@ -741,14 +741,20 @@ func (c customUnit) ParseExample(v string) (interface{}, error) { | |||
return fmt.Sprintf("%s USD", v), nil | |||
} | |||
|
|||
type customTime time.Time | |||
type customTime struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can stay as type customTime time.Time
, casting it to customTime
with customTime{t1}
should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this works. But the String
method then needs an extra cast (as c.Format
does not work):
func (c customTime) String() string {
return time.Time(c).Format("2006-01-02T15:04:05")
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly the String
method is an imlem detail, it's not even necessary for the sake of the tests.
Speaking of tests, I noticed that we don't test a custom type marshaling to JSON in the final spec. This would be nice to add as well, and perhaps a mention in the README that custom types MUST implement the json.Marshaler
interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohhh, I see what you mean. We had a bit different ideas on how this should work. My initial ideas was that the ParseExample
would already return the example value that can go into JSON on YAML directly (hence I also wrote some of the initial tests that way). That would be a formatted string in this case.
The way you are thinking about this is slightly different: ParseExample
returns a struct and then struct gets transformed into the final JSON or YAML representation through MarshalJSON()
and MarshalYAML()
methods, right?
Let me refactor the tests and add a note into the README.md.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ParseExample returns a struct
Not specifically a struct, it returns a value which type is customTime
(in this case), but that could be any concrete type, and yes the marshaling to JSON is implemented on that type.
That may seems counter-productive to not just return a string direclty that would be used in the final JSON of the spec, but what if the custom type has a int
kind for example. In this case, we most certainly would'nt want to marshal that custom type as a JSON string but as a JSON interger, most likely. Returning a concrete type from ParseExemple
and leaving the marshaling implem to the implem allows this while leaving full responsibility/flexibility to the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree this is better.
Almost there with the test, going to push shortly.
@nikicc One more comment, and we should be done. |
947919a
to
e602795
Compare
Squashed & pushed now.
Thanks! |
e602795
to
479114f
Compare
README.md
Outdated
@@ -344,6 +344,16 @@ Note that, according to the doc, the inherent version of the address is a semant | |||
|
|||
To help you write markdown descriptions in Go, a simple builder is available in the sub-package `markdown`. This is quite handy to avoid conflicts with backticks that are both used in Go for litteral multi-lines strings and code blocks in markdown. | |||
|
|||
#### Providing Examples for Structs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"for Structs" should be replaced by "custom types". They don't necesseraly have a Struct
kind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Fixed.
README.md
Outdated
@@ -223,7 +223,7 @@ You can use additional tags. Some will be interpreted by *tonic*, others will be | |||
| `description` | Add a description of the field in the spec. | | |||
| `deprecated` | Indicates if the field is deprecated. Accepted values are `1`, `t`, `T`, `TRUE`, `true`, `True`, `0`, `f`, `F`, `FALSE`. Invalid value are considered to be false. | | |||
| `enum` | A coma separated list of acceptable values for the parameter. | | |||
| `example` | An example value to be used in OpenAPI specification. | | |||
| `example` | An example value to be used in OpenAPI specification. See [section below](#Providing-Examples-for-Structs) for the demonstration on how to provide example for struct types. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"for custom types" as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -1276,6 +1276,13 @@ func parseExampleValue(t reflect.Type, value string) (interface{}, error) { | |||
return strconv.ParseFloat(value, t.Bits()) | |||
case reflect.Ptr: | |||
return parseExampleValue(t.Elem(), value) | |||
case reflect.Struct: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering again, since we now provide an interface, we should not restrict it to Struct
kind. Instead, we should honor the ParseExemple
method of every type that implement the Exampler
and call it to get the example value.
For types with a Struct
kind, we would default to a call to the ParseExample
method, if its implemented, otherwise return an empty string to signal that there is no example.
For others kinds, we would continue with the existing switch cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering again, since we now provide an interface, we should not restrict it to Struct kind. Instead, we should honor the ParseExemple method of every type that implement the Exampler and call it to get the example value.
Fixed.
For types with a Struct kind, we would default to a call to the ParseExample method, if its implemented, otherwise return an empty string to signal that there is no example.
Hmm, I'm not a fan of an empty string. I think it's better if we return the error (like we currently do). If someone provided an example
tag on a struct field, and the struct does not implement Exampler
I think an error is more appropriate than defaulting to empty string. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. The error doen't stop the spec generation anyway, so it's fine.
openapi/generator_test.go
Outdated
@@ -733,6 +734,27 @@ func TestSetServers(t *testing.T) { | |||
assert.Equal(t, servers, g.API().Servers) | |||
} | |||
|
|||
type customUnit struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to switch this custom type to an int
or float64
, to demonstrate and test that scalar go types that implement the Exampler
interface can also provide their own example value.
The type could still be a struct, if we wanted to have a more complex type, such as:
type customUnit struct {
value float64
currenty string
}
but that would be overkill for just tests, i'm just showcasing the possibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to float64
.
but that would be overkill for just tests, i'm just showcasing the possibility.
Yes. I think it's better to use float64
to demonstrate that this also works for non-structs.
openapi/types.go
Outdated
@@ -42,6 +42,12 @@ type DataType interface { | |||
Format() string | |||
} | |||
|
|||
// Exampler is the interface implemented by types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdyt of "Exampler is the interface implemented by custom types that can parse example values." ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds nice. Changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wI2L I addressed the latests comments but pushed in separate commits to ease the reviewing. Once we're happy with the implementation, I'll squash again. Let me know what you think.
README.md
Outdated
@@ -223,7 +223,7 @@ You can use additional tags. Some will be interpreted by *tonic*, others will be | |||
| `description` | Add a description of the field in the spec. | | |||
| `deprecated` | Indicates if the field is deprecated. Accepted values are `1`, `t`, `T`, `TRUE`, `true`, `True`, `0`, `f`, `F`, `FALSE`. Invalid value are considered to be false. | | |||
| `enum` | A coma separated list of acceptable values for the parameter. | | |||
| `example` | An example value to be used in OpenAPI specification. | | |||
| `example` | An example value to be used in OpenAPI specification. See [section below](#Providing-Examples-for-Structs) for the demonstration on how to provide example for struct types. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
README.md
Outdated
@@ -344,6 +344,16 @@ Note that, according to the doc, the inherent version of the address is a semant | |||
|
|||
To help you write markdown descriptions in Go, a simple builder is available in the sub-package `markdown`. This is quite handy to avoid conflicts with backticks that are both used in Go for litteral multi-lines strings and code blocks in markdown. | |||
|
|||
#### Providing Examples for Structs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Fixed.
openapi/types.go
Outdated
@@ -42,6 +42,12 @@ type DataType interface { | |||
Format() string | |||
} | |||
|
|||
// Exampler is the interface implemented by types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds nice. Changed.
openapi/generator_test.go
Outdated
@@ -733,6 +734,27 @@ func TestSetServers(t *testing.T) { | |||
assert.Equal(t, servers, g.API().Servers) | |||
} | |||
|
|||
type customUnit struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to float64
.
but that would be overkill for just tests, i'm just showcasing the possibility.
Yes. I think it's better to use float64
to demonstrate that this also works for non-structs.
@@ -1276,6 +1276,13 @@ func parseExampleValue(t reflect.Type, value string) (interface{}, error) { | |||
return strconv.ParseFloat(value, t.Bits()) | |||
case reflect.Ptr: | |||
return parseExampleValue(t.Elem(), value) | |||
case reflect.Struct: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering again, since we now provide an interface, we should not restrict it to Struct kind. Instead, we should honor the ParseExemple method of every type that implement the Exampler and call it to get the example value.
Fixed.
For types with a Struct kind, we would default to a call to the ParseExample method, if its implemented, otherwise return an empty string to signal that there is no example.
Hmm, I'm not a fan of an empty string. I think it's better if we return the error (like we currently do). If someone provided an example
tag on a struct field, and the struct does not implement Exampler
I think an error is more appropriate than defaulting to empty string. WDYT?
@nikicc LGTM, you can squash again. |
10fdc74
to
0b5d99a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0b5d99a
to
815e4ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikicc Thanks for the PR.
Fixes #72.