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

feat(fieldbehavior): Support optional keyword when validating REQUIRED fields #251

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

felipeparaujo
Copy link

@felipeparaujo felipeparaujo commented Sep 26, 2023

https://google.aip.dev/149 says the following on the optional keyword and the REQUIRED annotation:

The field behavior annotation and optional label are not mutually exclusive, because they address different problems. The former, google.api.field_behavior, focuses on communicating the server's perception of a field within the API e.g. if it is required or not, if it is immutable, etc. The latter, proto3's optional, is a wire format and code generation option that is strictly for toggling field presence tracking. While it might be confusing for a field to be simultaneously annotated with google.api.field_behavior = REQUIRED and labeled as optional, they are unrelated in practice and can reasonably be used together.

This is useful for distinguishing setting the field to its default value (0, false, or empty string) and not setting it at all.

This PR enables validating for this combination.

@felipeparaujo felipeparaujo requested a review from a team as a code owner September 26, 2023 09:34
@felipeparaujo felipeparaujo changed the title fieldbehavior: Validate optional fields correctly fieldbehavior: Validate optional fields Sep 26, 2023
@felipeparaujo felipeparaujo changed the title fieldbehavior: Validate optional fields fieldbehavior: Support optional keyword Sep 26, 2023
Copy link
Contributor

@alethenorio alethenorio left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution.

Please make sure that your commit message follows the conventional commit standard

It also looks like you have not explicitly enabled optinal support.

You should be able to do this in the main file, something like

plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)

@felipeparaujo felipeparaujo changed the title fieldbehavior: Support optional keyword @felipeparaujo feat(fieldbehavior): Support optional keyword when validating REQUIRED fields Sep 26, 2023
@felipeparaujo felipeparaujo changed the title @felipeparaujo feat(fieldbehavior): Support optional keyword when validating REQUIRED fields feat(fieldbehavior): Support optional keyword when validating REQUIRED fields Sep 26, 2023
@felipeparaujo
Copy link
Author

hi @alethenorio, thanks for the quick feedback. I've made the changes requested

Comment on lines +103 to +124
t.Run("missing optional field annotated with required", func(t *testing.T) {
t.Parallel()
assert.Error(
t,
ValidateRequiredFieldsWithMask(
&examplefreightv1.Site{},
&fieldmaskpb.FieldMask{Paths: []string{"personnel_count"}},
),
"missing required field: personnel_count",
)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

I am running this test without the above changes (other than enabling optional support) and it seems to pass for me.
Can you describe what kind of problem you ran into that prompted this PR? Is there a way to reproduce the issue in a test?

Copy link
Author

Choose a reason for hiding this comment

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

My bad, that test wasn't enough. I've added a happy path test that hopefully clears things up a bit.

The gist of the problem is that if a field is annotated with REQUIRED it can't be set to its default value, 0 in the case of an int64. The optional keyword allows for that by differentiating unset from default values. This is reflected in the generated code by making the fields pointers:

int64 personnel_count = 7;

generates

PersonnelCount int64 `protobuf:"varint,7,opt,name=personnel_count,json=personnelCount,proto3,oneof" json:"personnel_count,omitempty"`

whereas

optional int64 personnel_count = 7;

generates

PersonnelCount *int64 `protobuf:"varint,7,opt,name=personnel_count,json=personnelCount,proto3,oneof" json:"personnel_count,omitempty"`

Therefore, if a field uses the proto3 keyword optional but has a REQUIRED annotation, it needs to be validated as != nil, which allows for an int64 to be 0 in a request.

The use case I want to cover, specifically, is validating the following message:

message ExtentOrBoundingBox {
  // The position of a corner
  message Position {
    // X axis
    // We use optional on a required field to differentiate between 0 and unset.
    // For more info see: https://aip.dev/149#field-behavior-and-optional
    optional double x = 1 [(google.api.field_behavior) = REQUIRED];

    // Y axis
    // We use optional on a required field to differentiate between 0 and unset.
    // For more info see: https://aip.dev/149#field-behavior-and-optional
    optional double y = 2 [(google.api.field_behavior) = REQUIRED];

    // Z axis. Not required because this message could be used for a 2D representation too.
    optional double z = 3 [(google.api.field_behavior) = OPTIONAL];
  }

  // lower corner
  Position min = 1 [(google.api.field_behavior) = REQUIRED];

  // upper corner
  Position max = 2 [(google.api.field_behavior) = REQUIRED];
}

Here, x and y are required values that can be 0.0, the default value.

@felipeparaujo
Copy link
Author

@alethenorio @odsod PTAL

Copy link

This PR has been open for 180 days with no activity. Remove the stale label or add a comment or it will be closed in 14 days.

@github-actions github-actions bot added the Stale label May 20, 2024
@felipeparaujo
Copy link
Author

@alethenorio @odsod PTAL

@github-actions github-actions bot removed the Stale label May 27, 2024
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.

None yet

2 participants