-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
[Go] Optional values should be pointers #522
Comments
Would this be as simple to fix as changing a single line of a template? |
This would be a good update. It may be as simple as changing the model template. If so, you can just update it, rebuild the CLI and run it against your spec to see if it works. The template is here: Here are some steps to do this. For the example, I've assumed you have a fork and want to send a PR ;)
Use the resulting If so, then do the following to send a PR with the following: $ bin/go-petstore.sh
$ mvn verify -f samples/client/petstore/go
$ mvn verify
$ git add
$ git commit -m 'use pointers for optional properties'
$ bin/util/ensure-up-to-date
$ git push origin go/client/fix/optional-as-pointer If this is not in the petstore sample app, you can look into updating the sample Petshop Swagger 2.0 spec listed in If Even if you edit only the Then push to your fork and send a PR. |
@grokify Wow, thank you for the thorough instructions! I hope you have this saved as a "quick reply" 😄 I'd like to solve this, I'll report back here (within a week) if I'm able to. |
The fix is indeed one line. I have a branch here: https://github.com/siddharthist/openapi-generator/tree/go/client/fix/optional-as-pointer. I tested this with my project and it works fine. Unfortunately, it looks like it will take more effort than I can justify to fix the tests: many of the fields are optional and now must be converted to pointers. Example conversion in my branch. |
@siddharthist Thanks for looking into this. There aren't actually that many tests so it should be reasonably easy to update. The tests are located here:
|
@siddharthist I just put together a contribution quickstart here: Take a look. I'll also take a look at your change when I have time. |
Was this not resolved with the optional package? |
Make model optional value be pointer to enable distinguish omitting JSON fields and Go zero values. This approach cannot distinguish NotSet and Null. See: <OpenAPITools/openapi-generator#522> BREAKING CHANGE: model optional properties are pointer-ize, so partial model property types will be changed.
I noticed the generated Go package uses import "github.com/antihax/optional"
type UpdatePetWithFormOpts struct {
Name optional.String
Status optional.String
}
s := UpdatePetWithFormOpts{
Name: optional.NewString("foobar"),
} I found Google's approach more intuitive. They define a import "google.golang.org/cloud/internal/optional"
type UpdatePetWithFormOpts struct {
Name optional.String
Status optional.String
}
s := UpdatePetWithFormOpts{
Name: "foobar",
Status: nil,
} |
You lose compile time type checking by using The Google library there will panic at runtime if a wrong type is passed, which is a bad way to handle this. |
So I've just hit this issue and have a couple of observations:
If the 3 above points seem correct, I'd be happy to work on them; CC @antihax. |
Pointers for models could be a good short term solution, but i still think optional would be the way to implement. Marshaling should be trivial to implement and should also work on nested references. It should also be a cleaner way for the developer to know that they are dealing with a variable that may not be defined, since it will not compile if they directly reference. I.e.
would not compile and clearly show it is optional and need to be changed to
If it was a pointer, it would cause a panic at runtime if it was nil if not checked. @wing328 if we do decide to keep the optional package, can it be moved into the official repository? |
Ok, thanks for the response @antihax. I'll try to send a PR to |
So I've been trying to implement this and I'm having some difficulties. Perhaps someone can help me with this: Basically the defintion of the type of the struct member depends on two variables - whether or not this member is required and whether or not its nullable. I tried to do a little list summarizing what we could use in each case:
I'm not sure if I'm getting these right, but it seems to me that using |
Maybe my understanding is incorrect. Basic types cannot be null, but a Schema can make a type which is. We could have a schema that is
Maybe we need to generate the logic in the model struct to handle Should be possible to wrap it all into one struct and embed that within the models to reduce duplicate code. Are there any generators that are fully implementing this that we could lean on for an example? |
I think we may be at the point of overengineering this :) State of ArtI've been looking at some go api clients and have found that quite a few of them use a variation of a script called "gen-accessors.go" - I'm not sure where it originally came from, but it's used e.g. in the go-github client [1]. A simple github search shows that it's used quite widely [2]. (I think it was originally created as part of go-github, but I'm not sure). Generally, it seems that most go API clients (that I've seen) just use pointers everywhere (except for arrays and maps) in combination with FTR, it seems that gen-accessors.go has since been transformed into a standalone project that offers some customization [3] - but I don't think this project is used much, judging by number of Github stars. ProposalLet's start with making all the struct fields pointers and generate getters/setters for them, such as the ones generated by gen-accessors.go. Eventually, we can add further validation to these getters/setters to handle nullable/required cases. Pros
Cons
Does that sound reasonable? [1] https://github.com/google/go-github/blob/master/github/gen-accessors.go |
Reading through that it looks like it just makes it convenient to deference values and doesn't help differentiate from null or optional values. I.e. It creates these functions:
We would also need Go installed on the system running codegen. Looking at some of the other generators, it appears they are wrapping the types. The JavaSpring generator uses: https://github.com/OpenAPITools/jackson-databind-nullable/blob/master/src/main/java/org/openapitools/jackson/nullable/JsonNullable.java
This is very similar to how the sql package handles NULL in golang which looks like this:
We also have to handle nullable structs/models which the sql package does not. |
So I wasn't suggesting we use the gen-accessors.go, but rather generate the same output with our templates, so we wouldn't need go installed on the system running codegen. And yeah, I know this is not ideal, but I'm just failing to find a good way to do all this. I'm going to keep searching; if you have any specific proposals on how to make this all work, I'd be very interested to hear them. Edit: I think we could use the approach that I suggested (make everything a pointer and create getters/setters) and combine it with custom Marshal methods for all structures. The custom marshalling would return errors if some of the conditions specified by the OpenAPI spec (nullable/required/...) were not met. Would that make sense? |
FTR, my PR that aims to fix this - #3371 - was merged to the |
@bkabrda : Which version of openapi-gen, I have to use to get his |
@EleanorRigby 4.0.3 release happened before my PR for this was merged, so you'll either need to wait for a new release or do a testing build yourself. |
@bkabrda, @wing328 as per v4.1.0 optional values of structs created to act as container for optional path and body parameters still uses the Example: Optional approachtype LoginUserOpts struct {
Credentials optional.Interface
}
func (a *UsersApiService) LoginUser(ctx context.Context, localVarOptionals *LoginUserOpts) (*http.Response, error) {
...
} Pointer approachtype LoginUserOpts struct {
Credentials *Credentials
}
func (a *UsersApiService) LoginUser(ctx context.Context, localVarOptionals *LoginUserOpts) (*http.Response, error) {
...
} |
@bkabrda PR that you referenced makes all fields pointer, that makes developer experience pretty bad as it requires wrapping every field with |
@qmuntal yeah, I guess this would make sense to do, good point. @shaxbee so IIUC you're saying that pointers shouldn't be used for maps/slices? I think that's a fair point and worth looking into. Unfortunately I don't have time to work on any of these features ATM, but I know that @zippolyte is going to be working with the go-experimental templates, so he might be able to help. |
Description
If a field of a struct isn't specified as
required
, then it should be a pointer to that value so that one can differentiate between unset by the user or set to the "zero value". See the following Go playground: https://play.golang.org/p/85r_JbH5XVrSee also e.g. this Stackoverflow answer.
openapi-generator version
Docker container
OpenAPI declaration file content or url
Related issues/PRs
Suggest a fix/enhancement
The text was updated successfully, but these errors were encountered: