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 optional info for google.proto types #6153

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

khj68
Copy link

@khj68 khj68 commented Mar 11, 2025

Motivation:

In the HTTP documentation generated by Armeria, all google.protobuf.* types (such as google.protobuf.StringValue) are incorrectly marked as required: true. This caused confusion when collaborating with frontend teams, as optional fields were not clearly distinguished.

Modifications:

  • Added explicit handling for google.protobuf.* wrapper types to correctly mark them as optional.
  • Updated the logic to ensure that optional parameters are properly reflected in the documentation.

Result:

@CLAassistant
Copy link

CLAassistant commented Mar 11, 2025

CLA assistant check
All committers have signed the CLA.

@minwoox minwoox added the defect label Mar 11, 2025
@minwoox minwoox added this to the 1.32.1 milestone Mar 11, 2025
Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Basically looks good. 👍

@@ -418,6 +419,22 @@ private static Map<String, Field> buildFields(Descriptor desc,
return builder.buildKeepingLast();
}

private static boolean isOptionalFieldType(FieldDescriptor field) {
Copy link
Contributor

Choose a reason for hiding this comment

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

probably isScalarValueWrapperMessage?
https://protobuf.dev/programming-guides/proto3/#scalar
Could you also call this method from the line 454?

Copy link
Author

Choose a reason for hiding this comment

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

That name is much better.
I’ve applied it to line 454 as well to remove duplicate code.
Thanks for the suggestion.

this.type = requireNonNull(type, "type");
this.isRepeated = isRepeated;
this.isOptional = isOptional;
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably we can do this.isOptional = isOptional || isRepeated; here

Copy link
Author

Choose a reason for hiding this comment

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

Great. I've applied it 👍

break;
case MESSAGE:
@Nullable
final JavaType wellKnownFieldType = getJavaTypeForWellKnownTypes(field);
final boolean isOptional = isOptionalFieldType(field);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we use field.isOptional(); here?

Copy link
Author

Choose a reason for hiding this comment

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

field.isOptional() only checks if the field has LABEL_OPTIONAL set, which is not sufficient to determine whether it’s a google.protobuf.StringValue or similar wrapper type. Since wrapper types in Protobuf do not explicitly have LABEL_OPTIONAL, we need a separate function to correctly identify them.

Let me know if I might be misunderstanding something 🙇

Copy link
Contributor

@minwoox minwoox Mar 11, 2025

Choose a reason for hiding this comment

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

I realized that all fields are optional in proto3:
https://protobuf.dev/programming-guides/proto3/#field-labels
What do you think of making the fields required only when [(google.api.field_behavior) = REQUIRED]; used instead?
https://google.aip.dev/203

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for providing the documentation references.
Since maintaining backward compatibility is important, using [(google.api.field_behavior) = REQUIRED] as the criteria for required fields makes sense.
I’ve updated the implementation accordingly 👍

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

This looks great, thanks!
One last request—could you add a test case to verify the change?

@minwoox minwoox modified the milestones: 1.32.1, 1.33.0 Mar 12, 2025
@@ -451,7 +451,11 @@ void httpEndpoint() {
FieldInfo.builder("message_id", TypeSignature.ofBase(JavaType.STRING.name()))
.location(FieldLocation.PATH).requirement(FieldRequirement.REQUIRED).build(),
FieldInfo.builder("text", TypeSignature.ofBase(JavaType.STRING.name()))
.location(FieldLocation.BODY).requirement(FieldRequirement.REQUIRED).build()));
.location(FieldLocation.BODY).requirement(FieldRequirement.REQUIRED).build(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this optional?

Copy link
Author

@khj68 khj68 Mar 12, 2025

Choose a reason for hiding this comment

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

Java String type should be Required without behavior I guess 🙇

Copy link
Author

Choose a reason for hiding this comment

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

image

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we agreed that fields are optional by default if REQUIRED is not specified. 😆
https://protobuf.dev/programming-guides/proto3/#field-labels

I think this should be false
https://github.com/line/armeria/pull/6153/files#diff-a9d03eb106960c77af7b39790659dfa479f644ef0745c2e0075e56a5de8badacR373

Copy link
Contributor

Choose a reason for hiding this comment

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

What I meant is doing something like:
builder.put(key, new Field(field, parentNames, field.getJavaType(), hasRequiredFieldBehavior(field)));

Copy link
Author

@khj68 khj68 Mar 12, 2025

Choose a reason for hiding this comment

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

I see.
Then, if in this case
string text = 1 [(google.api.field_behavior) = REQUIRED];

it should be Required, right?

so, it only depends on field_behavior, is it right? 🙇

Copy link
Contributor

Choose a reason for hiding this comment

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

That's correct. 👍 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we agreed that fields are optional by default if REQUIRED is not specified.

Some people may want to make the fields required by default. Should we provide a configuration to override the default behavior?

Copy link
Contributor

Choose a reason for hiding this comment

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

Some people may want to make the fields required by default.

Can we do this when there's a clear demand for it? I really hope users follow the AIP if they want to make it required.
Of course, there could be issues if the users rely on third-party-provided protobufs, but even in that case,
I don't think we should make all the fields required.

.getOptions()
.getExtension(FieldBehaviorProto.fieldBehavior);
return fieldBehaviors.contains(FieldBehavior.REQUIRED);
} catch (NoSuchMethodError error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

When could NoSuchMethodError be raised?

Copy link
Author

Choose a reason for hiding this comment

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

./gradlew :it:grpc:protobuf4:shadedTest --info

In this case, protobuf4 throw NoSuchMethodError.

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice if we can support [(google.api.field_behavior) = REQUIRED] for protobuf 4 version.

Copy link
Author

@khj68 khj68 Mar 12, 2025

Choose a reason for hiding this comment

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

image

okay, then I should find other logic. because in protobuf4, GeneratedMessageV3 is deprecated.
So I can't use getExtension function.
I'll try other approach 🙇
but it was really hard to find.. do you have another idea?

Copy link
Contributor

Choose a reason for hiding this comment

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

We can fix it:

private static boolean hasRequiredFieldBehavior(FieldDescriptor field) {
    if (field.isRepeated()) {
        return false;
    }

    final List<FieldBehavior> fieldBehaviors = field
            .getOptions()
            .getExtension((ExtensionLite<FieldOptions, List<FieldBehavior>>)
                                  FieldBehaviorProto.fieldBehavior);
    return fieldBehaviors.contains(FieldBehavior.REQUIRED);
}

Copy link
Author

Choose a reason for hiding this comment

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

omg thanks..!
I'll try it.

@minwoox minwoox modified the milestones: 1.33.0, 1.32.1 Mar 12, 2025
minwoox

This comment was marked as duplicate.

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Nice work, @khj68! 👍

@khj68
Copy link
Author

khj68 commented Mar 12, 2025

@minwoox @ikhoon
Thank you for your detailed review. 👍

I appreciate your insights, and I hope to have another opportunity to contribute in the future.
Thanks again!

@minwoox
Copy link
Contributor

minwoox commented Mar 12, 2025

I hope to have another opportunity to contribute in the future.

Hope to see you again. 😆

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

Successfully merging this pull request may close these issues.

4 participants