-
Notifications
You must be signed in to change notification settings - Fork 20
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
feat(fieldbehavior): ignore immutable in validation if not in field mask #266
Conversation
@@ -170,7 +184,7 @@ func TestValidateImmutableFieldsWithMask(t *testing.T) { | |||
ExternalReferenceId: "I am immutable!", | |||
}, | |||
UpdateMask: &fieldmaskpb.FieldMask{ | |||
Paths: []string{""}, | |||
Paths: []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.
The fieldmask in this test case should be completely empty. ""
is also considered a value in most other cases in the aip-go library. Most importantly it aligns with this case in fieldmask.Update
:
// Special-case: No update mask.
// Update all fields of src that are set on the wire.
case len(mask.GetPaths()) == 0:
updateWireSetFields(dstReflect, srcReflect)
...
fieldbehavior/immutable.go
Outdated
@@ -29,7 +29,7 @@ func validateImmutableFields(m protoreflect.Message, mask *fieldmaskpb.FieldMask | |||
} | |||
|
|||
currPath += string(field.Name()) | |||
if isImmutable(field) && (hasPath(mask, currPath) || m.Has(field)) { | |||
if isImmutable(field) && (hasPath(mask, currPath) || (isEmpty(mask) && m.Has(field))) { |
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.
Your intention here is to treat empty update mask the same way as a wildcard, right?
If so, two thoughts:
I'm wondering if we should move the isEmpty
logic to inside hasPath
instead?
I think we should only treat empty update mask as wildcard if the update mask field is optional (not annotated with REQUIRED
). WDYT?
Refer to AIP-134
The field may be required or optional. If it is required, it must include the corresponding annotation. If optional, the service must treat an omitted field mask as an implied field mask equivalent to all fields that are populated (have a non-empty value).
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 is the intention.
-
It does seem to make sense, we would just have to judge the implications on the other fieldbehavior functions as well, but either way it would most likely make sense for them to have the same behavior.
-
Absolutely a valid point! If we decide to incorporate it into
hasPath
it would make sense there as well IMO.
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.
@radhus After reviewing the code for this validation, the fieldmask.Update
and fieldbehavior.Required
validation i have some concerns:
- [Done] It would be possible to add the
isEmpty
check tohasPath
since the only other use of the function handles that case
// If no paths are provided, the field mask should be treated to be equivalent
// to all fields set on the wire. This means that no required fields can be missing,
// since if they were missing they're not set on the wire.
if len(mask.GetPaths()) == 0 {
return nil
}
-
The library is consistently interpreting empty
fieldmask
as if it was wildcard,*
, introducing the change in the library should probably be applied in a broader way and not just on one specific validation, WDYT? -
The
fieldbehavior
of thefieldmask
is linked to theFieldDescriptor
on the parent message. With the way the function is implemented and howfieldmask
works/is evaluated it is not required that the message passed as an argument is the actual request. It is possible today to use it with just the update value instead, something like this:
fieldbehavior.ValidateImmutableFieldsWithMask(
req.GetShipment(),
req.GetUpdateMask(),
)
As the use of an actual request message has not been enforced in any way, the evaluation of fieldbehavior_REQUIRED
on the update_mask
would have to be permissive to it not being included in the message parameter to the function or it would risk breaking the functionality. This would not fully defeat the purpose of the check, but it would somewhat dampen the effect it should have.
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 library is consistently interpreting empty
fieldmask
as if it was wildcard,*
, introducing the change in the library should probably be applied in a broader way and not just on one specific validation, WDYT?
Then I think make sense to keep being consistent and introduce a separate (potentially breaking) change later that respects the field behavior on the field mask, if we want that!
3a9eb2b
to
8b2207e
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.
Looks good to me, nice! ⭐
fieldbehavior/immutable.go
Outdated
@@ -29,7 +29,7 @@ func validateImmutableFields(m protoreflect.Message, mask *fieldmaskpb.FieldMask | |||
} | |||
|
|||
currPath += string(field.Name()) | |||
if isImmutable(field) && (hasPath(mask, currPath) || m.Has(field)) { | |||
if isImmutable(field) && (hasPath(mask, currPath) || (isEmpty(mask) && m.Has(field))) { |
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 library is consistently interpreting empty
fieldmask
as if it was wildcard,*
, introducing the change in the library should probably be applied in a broader way and not just on one specific validation, WDYT?
Then I think make sense to keep being consistent and introduce a separate (potentially breaking) change later that respects the field behavior on the field mask, if we want that!
AIP-203 "When a service receives an immutable field in an update request (or similar), even if included in the update mask, the service should ignore the field if the value matches, but should error with INVALID_ARGUMENT if a change is requested"
8b2207e
to
2cff87c
Compare
This PR adds an additional check to the validation of
IMMUTABLE
fields to see whether or not the provided fieldmask is empty before checking if the immutable field is part of the message.This to match the case in AIP-203 that states the service should ignore the field unless a change is requested. Previously, the validation would error any time the immutable field was included in the message, without regarding if an actual update was requested.