-
Notifications
You must be signed in to change notification settings - Fork 937
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
base: main
Are you sure you want to change the base?
Conversation
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.
Basically looks good. 👍
@@ -418,6 +419,22 @@ private static Map<String, Field> buildFields(Descriptor desc, | |||
return builder.buildKeepingLast(); | |||
} | |||
|
|||
private static boolean isOptionalFieldType(FieldDescriptor 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.
probably isScalarValueWrapperMessage
?
https://protobuf.dev/programming-guides/proto3/#scalar
Could you also call this method from the line 454?
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.
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; |
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.
Probably we can do this.isOptional = isOptional || isRepeated;
here
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.
Great. I've applied it 👍
break; | ||
case MESSAGE: | ||
@Nullable | ||
final JavaType wellKnownFieldType = getJavaTypeForWellKnownTypes(field); | ||
final boolean isOptional = isOptionalFieldType(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.
Can't we use field.isOptional();
here?
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.
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 🙇
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 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
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.
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 👍
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.
This looks great, thanks!
One last request—could you add a test case to verify the change?
@@ -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(), |
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.
Isn't this optional?
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.
Java String type should be Required without behavior I guess 🙇
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.
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 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
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.
What I meant is doing something like:
builder.put(key, new Field(field, parentNames, field.getJavaType(), hasRequiredFieldBehavior(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.
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? 🙇
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.
That's correct. 👍 👍
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 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?
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.
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) { |
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.
When could NoSuchMethodError
be raised?
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.
./gradlew :it:grpc:protobuf4:shadedTest --info
In this case, protobuf4 throw NoSuchMethodError.
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.
It would be nice if we can support [(google.api.field_behavior) = REQUIRED]
for protobuf 4 version.
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.
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 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);
}
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.
omg thanks..!
I'll try 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.
Nice work, @khj68! 👍
Hope to see you again. 😆 |
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:
Result:
This change ensures that google.protobuf.* wrapper types are correctly marked as optional in the generated HTTP documentation.
Improves clarity for frontend and backend collaboration by accurately distinguishing required and optional fields.
Closes Clarifying Required and Optional Fields in Armeria Docs (Proto) #6152