-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Update guidance on required field serialization #8486
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
base: master
Are you sure you want to change the base?
Update guidance on required field serialization #8486
Conversation
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: JoelSpeed The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Thinking through different types we might see, and the implications of this suggestion NullableAnything that needs to be a pointer to allow the zero value to be set, that also has StringAny required string should have a minimum length. If that minimum length is 0, or unset, then the string should be a pointer+omitempty to allow the empty string to be an explicit choice. Except when the required string is not actually a string.
Integer/FloatAs per example in the decsription, if the 0 is in the valid range of values (based on minimum/maximum markers) then pointer and omitempty should be used to allow BoolAny required bool should be a pointer+omitempty unless validation only allows the value ListsA required list should have a minimum length validation ( If the list wasn't a pointer, but had omitempty, the structured client would not be able to explicitly set an empty list. MapsThe same as lists? Not sure what the appropriate marker is for the minimum size of a map (it's ObjectsFor the zero value to be valid, an object should have no required fields, and have no validation that requires a minimum number of fields be set (e.g. If the object does have a required field, but that required field would marshal the zero value and that would be accepted by validation, then the zero value of the struct would also be considered valid. I think this would happen generally on a poorly validated set of fields. E.g. a string that doesn't have an explicit minimum length, but has not been added based on the rules outlined here. The object |
@@ -843,10 +843,10 @@ having the `+optional` comment tag is highly recommended. | |||
Required fields have the opposite properties, namely: | |||
|
|||
- They do not have an `+optional` comment tag. | |||
- They do not have an `omitempty` struct tag. | |||
- They are not a pointer type in the Go definition (e.g. `AnotherFlag SomeFlag`). | |||
- They mark themselves as required explicitly with a `+required` comment tag, or implicitly by not setting an `omitempty` struct tag. |
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 don't have enough context, but could we simplify this guidance to required fields are only marked via the +required marker (given that's how it should be)
Or at least recommend it
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.
+1 - explicit FTW. If declarative validation flies, I want explicit.
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 also believe in being explicit, but I know some tooling is implicitly assuming this, and we have a long history of that implicitness.
If we say they should be explicitly marked, should we also add an N.B.
that explains that historically this has been implicitly detected?
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.
If we say they should be explicitly marked, should we also add an N.B. that explains that historically this has been implicitly detected?
I was thinking something like this, yes. Recommend to be explicit and then mention in some way how some tools behave (probably these tools (thinking about controller-gen as an example) also won't change to avoid breaking changes)
/hold I've been through this with @liggitt and we need to make some more updates to the guidance. Will look at creating a decision tree to help folks understand whether or not they should be setting fields as pointers/omitempty etc |
(notes from our discussion in https://docs.google.com/document/d/1k3XxUTEjcQifSXjoy5CcQY9QttdhQ-EG-R3vrKq6SUc/edit?pli=1&resourcekey=0-5xMYchKvoisABPw9tsR3nA&tab=t.0#bookmark=id.ks1ziis4vgzz - shared with https://groups.google.com/a/kubernetes.io/g/dev, can join that to get read access) |
- The API server should not allow POSTing or PUTing a resource with this field | ||
unset. | ||
- They _typically_ do not use pointer types in the Go definition (e.g. `AnotherFlag SomeFlag`), though required fields where the zero value is a valid value may use pointer types, likely paired with an `omitempty` struct tag to avoid spurious null serializations. |
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.
"may" or "must" ?
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 this is must
and likely paired
, should remove likely
c2ee563
to
8856289
Compare
8856289
to
22f7099
Compare
22f7099
to
a40a583
Compare
@JoelSpeed thanks for this PR, this clarifications are really helpful! |
- The API server should not allow POSTing or PUTing a resource with this field | ||
unset. | ||
- They _typically_ do not use pointer types in the Go definition (e.g. `AnotherFlag SomeFlag`), though required fields where the zero value is a valid value must use pointer types, paired with an `omitempty` struct tag to avoid spurious null serializations. | ||
|
||
For more details on how to use pointers and `omitempty` with fields, see [Serialization of optional/required fields](#serialization-of-optionalrequired-fields). | ||
|
||
Using the `+optional` or the `omitempty` tag causes OpenAPI documentation 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.
q: should this (openAPI documentation) change now that we can have omitempty on required fields?
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.
We will need to make sure that it respects +required
tags. If the field doesn't have +required
and has omitempty
, then I think there are many tools that still assume optional and we shouldn't change that.
In the long run, we need to make sure all fields are marked as +optional
xor +required
explicitly
@liggitt and I were discussing serialization of required fields in a thread and realised that there are valid cases where a required field should be a pointer and should be omitempty.
Take the example:
Here, the value
0
is a valid user choice.For a structured Go client, if the field were not a pointer, and not omitempty per the existing guidance, then the structured client is not actually required to set a value for the field, as the json marshalling process will marshal
foo: 0
and set a choice for them.This defeats the purpose of the field being required for structured clients, and creates a discrepancy in behaviour between structured and unstructured clients.
Would appreciate thoughts of other API reviewers on this
CC @thockin @deads2k