Skip to content
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

Add omitempty for pointer input fields #264

Closed
wants to merge 1 commit into from

Conversation

dharijanto
Copy link

When optional: "pointer" is configured, omitempty is needed for input fields for the generated code to be semantically correct. Without omitempty an empty pointer would generate an "empty value" which is incorrect.

Consider the following graphql schema generated by Hasura:

input task_insert_input {
  id: Int
}

Without this fix, the following golang struct will be generated:

type Task_insert_input struct {
  Id: *int `json:"id"`
}

When we populate the struct without Id being set, it'd serialize to { id: 0 } as oppose to {} (i.e. so that the id would be generated by Hasura)

I have:

  • Written a clear PR title and description (above)
  • Signed the Khan Academy CLA
  • Added tests covering my changes, if applicable
  • Included a link to the issue fixed, if applicable
  • Included documentation, for new features
  • Added an entry to the changelog

When `optional: "pointer"` is configured, `omitempty` is needed for input
fields for the generated code to be semantically correct. Without `omitempty`
an empty pointer would generate an "empty value" which is incorrect.

Consider the following graphql schema generated by Hasura:

```
input task_insert_input {
  id: Int
}
```

Without this fix, the following golang struct will be generated:
```
type Task_insert_input struct {
  Id: *int `json:"id"`
}
```

When we populate the struct without `Id` being set, it'd serialize to
`{ id: 0 }` as oppose to `{}` (i.e. so that the id would be generated by Hasura)
@dharijanto
Copy link
Author

Created for this: #260

@@ -439,6 +439,11 @@ func (g *generator) convertDefinition(
return nil, err
}

if options.GetPointer() || (!field.Type.NonNull && g.Config.Optional == "pointer") {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should apply to non-input structures as well..?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omitempty is meaningless for outputs (since it's meaningless on unmarshal)

Copy link
Collaborator

@benjaminjkraft benjaminjkraft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think as-is this is in some sense a breaking change, although perhaps anyone who cares about this prefers the new behavior? It also seems confusing to do this only and automatically for optional: pointer, which right now is I think otherwise equivalent to # @genqlient(pointer: true) on every optional field. Maybe we add a new config optional: pointer_and_omitempty and handle both issues that way?

(Edit: Thinking more, doing a new config doesn't strictly handle the second issue -- you could want this for non-pointer: true fields. But I guess you can always annotate those individually, and probably you never want it for non-pointer fields globally (since that means false gets omitted). So I guess it's still maybe the best compromise.)

BTW, you'll need to update the tests, docs, changelog, etc. -- see the PR message for the checklist.

Comment on lines +443 to +444
oe := true
fieldOptions.Omitempty = &oe
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to mutate fieldOptions -- just compute the correct value and then use it on line 453.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A belated realization: actually the logic here is also wrong: users may need to override the global option locally, if they do need to pass an explicit null. (Sadly in Go, as bad as it is to represent "T or null", it's even worse to represent "T or null or unset", which to be fair is also true of almost any language that's not JavaScript.) That is, if you set this option, then you can do # @genqlient(omitempty: false) if you do want explicit null for some query.

So that means you'll need to actually look at the value of fieldOptions.Omitempty and then only use this logic if it's null, I think.

Copy link

@obitech obitech Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benjaminjkraft there doesn't seem to be much movement here on your feedback but I really really need this feature so I've created #287 with your suggestion, in order to get things forward. Feel free to take a look over there :)

@@ -439,6 +439,11 @@ func (g *generator) convertDefinition(
return nil, err
}

if options.GetPointer() || (!field.Type.NonNull && g.Config.Optional == "pointer") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omitempty is meaningless for outputs (since it's meaningless on unmarshal)

@partounian
Copy link

partounian commented May 26, 2023

This would be super awesome. The GraphQL server I'm interacting with has deeply nested inputs and without this feature it breaks.

Seems like use_struct_references: false and optional: pointer in the yaml might improve my issue. Lots of types need to be updated, so code is getting messy, but maybe later I can see how to clean this up.

@StevenACoffman StevenACoffman removed their request for review October 12, 2023 18:38
@benjaminjkraft
Copy link
Collaborator

Closing in favor of #308 which uses a config option

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants