Skip to content

Commit 81dd15f

Browse files
committed
fix(generator): use json_name when available
1 parent 3deec8a commit 81dd15f

9 files changed

Lines changed: 97 additions & 5 deletions

File tree

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/HttpJsonServiceStubClassComposer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,9 @@ private Expr createFieldsExtractorClassInstance(
926926
paramsPutArgs.add(
927927
ValueExpr.withValue(
928928
StringObjectValue.withValue(
929-
JavaStyle.toLowerCamelCase(httpBindingFieldName.name()))));
929+
(httpBindingFieldName.jsonName() != null)
930+
? httpBindingFieldName.jsonName()
931+
: JavaStyle.toLowerCamelCase(httpBindingFieldName.name()))));
930932
paramsPutArgs.add(requestBuilderExpr);
931933

932934
Expr paramsPutExpr =

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/Field.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ public abstract class Field {
3131
// resolution behavior. For more context, please see the invocation site of the setter method.
3232
public abstract String originalName();
3333

34+
@Nullable
35+
public abstract String jsonName();
36+
3437
public abstract TypeNode type();
3538

3639
// If the field is annotated with google.api.field_behavior = REQUIRED, then this is true. This is
@@ -92,6 +95,7 @@ public boolean equals(Object o) {
9295
Field other = (Field) o;
9396
return name().equals(other.name())
9497
&& originalName().equals(other.originalName())
98+
&& Objects.equals(jsonName(), other.jsonName())
9599
&& type().equals(other.type())
96100
&& isRequired() == other.isRequired()
97101
&& fieldInfoFormat() == other.fieldInfoFormat()
@@ -109,6 +113,7 @@ && isProto3Optional() == other.isProto3Optional()
109113
public int hashCode() {
110114
return 17 * name().hashCode()
111115
+ 31 * originalName().hashCode()
116+
+ (jsonName() == null ? 0 : jsonName().hashCode())
112117
+ 19 * type().hashCode()
113118
+ (isMessage() ? 1 : 0) * 23
114119
+ (isEnum() ? 1 : 0) * 29
@@ -141,6 +146,8 @@ public abstract static class Builder {
141146

142147
public abstract Builder setOriginalName(String originalName);
143148

149+
public abstract Builder setJsonName(String jsonName);
150+
144151
public abstract Builder setType(TypeNode type);
145152

146153
public abstract Builder setIsRequired(boolean isRequired);

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/HttpBindings.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public abstract static class HttpBinding implements Comparable<HttpBinding> {
4242

4343
abstract String lowerCamelName();
4444

45+
// The dot-separated json_name of the field.
46+
// e.g. parent.iceberg-catalog-id
47+
@Nullable
48+
public abstract String jsonName();
49+
4550
// An object that contains all info of the leaf level field
4651
@Nullable
4752
public abstract Field field();
@@ -70,6 +75,8 @@ public abstract static class Builder {
7075

7176
public abstract HttpBindings.HttpBinding.Builder setName(String name);
7277

78+
public abstract HttpBindings.HttpBinding.Builder setJsonName(String jsonName);
79+
7380
public abstract HttpBindings.HttpBinding.Builder setField(Field field);
7481

7582
abstract HttpBindings.HttpBinding.Builder setLowerCamelName(String lowerCamelName);

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/HttpRuleParser.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.api.generator.gapic.model.HttpBindings;
2222
import com.google.api.generator.gapic.model.HttpBindings.HttpBinding;
2323
import com.google.api.generator.gapic.model.Message;
24+
import com.google.api.generator.gapic.utils.JavaStyle;
2425
import com.google.api.pathtemplate.PathTemplate;
2526
import com.google.common.base.Preconditions;
2627
import com.google.common.base.Strings;
@@ -29,8 +30,10 @@
2930
import com.google.common.collect.Sets;
3031
import com.google.protobuf.DescriptorProtos.MethodOptions;
3132
import com.google.protobuf.Descriptors.MethodDescriptor;
33+
import java.util.ArrayList;
3234
import java.util.Collections;
3335
import java.util.HashMap;
36+
import java.util.List;
3437
import java.util.Map;
3538
import java.util.Optional;
3639
import java.util.Set;
@@ -144,10 +147,25 @@ private static Set<HttpBinding> validateAndConstructHttpBindings(
144147
continue;
145148
}
146149
Message nestedMessage = inputMessage;
150+
List<String> jsonNameParts = new ArrayList();
147151
for (int i = 0; i < subFields.length; i++) {
148152
String subFieldName = subFields[i];
153+
154+
Field field = nestedMessage.fieldMap().get(subFieldName);
155+
Preconditions.checkState(
156+
field != null,
157+
"Expected message %s to contain field %s but none found",
158+
nestedMessage.name(),
159+
subFieldName);
160+
161+
// Each component of the JSON name uses the json_name annotation of the field,
162+
// or default to the field name
163+
jsonNameParts.add(
164+
!Strings.isNullOrEmpty(field.jsonName())
165+
? field.jsonName()
166+
: JavaStyle.toLowerCamelCase(field.name()));
167+
149168
if (i < subFields.length - 1) {
150-
Field field = nestedMessage.fieldMap().get(subFieldName);
151169
nestedMessage = messageTypes.get(field.type().reference().fullName());
152170
Preconditions.checkNotNull(
153171
nestedMessage,
@@ -160,9 +178,12 @@ private static Set<HttpBinding> validateAndConstructHttpBindings(
160178
checkHttpFieldIsValid(subFieldName, nestedMessage, false);
161179
patternSampleValue = patternSampleValues.get(paramName);
162180
}
163-
Field field = nestedMessage.fieldMap().get(subFieldName);
164181
httpBindings.add(
165-
httpBindingBuilder.setValuePattern(patternSampleValue).setField(field).build());
182+
httpBindingBuilder
183+
.setValuePattern(patternSampleValue)
184+
.setField(field)
185+
.setJsonName(String.join(".", jsonNameParts))
186+
.build());
166187
}
167188
}
168189
}

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,7 @@ private static Field parseField(
11811181
return fieldBuilder
11821182
.setName(actualFieldName)
11831183
.setOriginalName(fieldDescriptor.getName())
1184+
.setJsonName(fieldDescriptor.getJsonName())
11841185
.setType(TypeParser.parseType(fieldDescriptor))
11851186
.setIsMessage(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE)
11861187
.setIsEnum(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.ENUM)

sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/rest/goldens/HttpJsonComplianceStub.golden

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ public class HttpJsonComplianceStub extends ComplianceStub {
9494
Map<String, List<String>> fields = new HashMap<>();
9595
ProtoRestSerializer<RepeatRequest> serializer =
9696
ProtoRestSerializer.create();
97+
serializer.putQueryParam(
98+
fields, "custom-kebab-name", request.getCustomKebabName());
9799
serializer.putQueryParam(fields, "name", request.getName());
98100
serializer.putQueryParam(
99101
fields, "serverVerify", request.getServerVerify());
@@ -132,6 +134,8 @@ public class HttpJsonComplianceStub extends ComplianceStub {
132134
Map<String, List<String>> fields = new HashMap<>();
133135
ProtoRestSerializer<RepeatRequest> serializer =
134136
ProtoRestSerializer.create();
137+
serializer.putQueryParam(
138+
fields, "custom-kebab-name", request.getCustomKebabName());
135139
serializer.putQueryParam(fields, "info", request.getInfo());
136140
serializer.putQueryParam(fields, "name", request.getName());
137141
serializer.putQueryParam(
@@ -181,6 +185,8 @@ public class HttpJsonComplianceStub extends ComplianceStub {
181185
Map<String, List<String>> fields = new HashMap<>();
182186
ProtoRestSerializer<RepeatRequest> serializer =
183187
ProtoRestSerializer.create();
188+
serializer.putQueryParam(
189+
fields, "custom-kebab-name", request.getCustomKebabName());
184190
serializer.putQueryParam(fields, "info", request.getInfo());
185191
serializer.putQueryParam(fields, "name", request.getName());
186192
serializer.putQueryParam(
@@ -228,6 +234,8 @@ public class HttpJsonComplianceStub extends ComplianceStub {
228234
Map<String, List<String>> fields = new HashMap<>();
229235
ProtoRestSerializer<RepeatRequest> serializer =
230236
ProtoRestSerializer.create();
237+
serializer.putQueryParam(
238+
fields, "custom-kebab-name", request.getCustomKebabName());
231239
serializer.putQueryParam(fields, "info", request.getInfo());
232240
serializer.putQueryParam(fields, "name", request.getName());
233241
serializer.putQueryParam(
@@ -272,6 +280,8 @@ public class HttpJsonComplianceStub extends ComplianceStub {
272280
Map<String, List<String>> fields = new HashMap<>();
273281
ProtoRestSerializer<RepeatRequest> serializer =
274282
ProtoRestSerializer.create();
283+
serializer.putQueryParam(
284+
fields, "custom-kebab-name", request.getCustomKebabName());
275285
serializer.putQueryParam(fields, "info", request.getInfo());
276286
serializer.putQueryParam(fields, "name", request.getName());
277287
serializer.putQueryParam(

sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/model/HttpBindingsTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,12 @@ void isEnum_shouldReturnTrueIfFieldExistsAndIsEnumIsTue() {
9696

9797
Truth.assertThat(httpBinding.isEnum()).isTrue();
9898
}
99+
100+
@Test
101+
void builder_preservesLiteralJsonName() {
102+
final String jsonName = "iceberg-catalog-id";
103+
HttpBinding binding =
104+
HttpBinding.builder().setName("doesNotMatter").setJsonName(jsonName).build();
105+
Truth.assertThat(binding.jsonName()).isEqualTo(jsonName);
106+
}
99107
}

sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/HttpRuleParserTest.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,15 @@ void parseHttpAnnotation_shouldPutAllFieldsIntoQueryParamsIfPathParamAndBodyAreN
114114
HttpBindings actual = HttpRuleParser.parse(rpcMethod, inputMessage, messages);
115115

116116
HttpBinding expected1 =
117-
HttpBinding.builder().setName("name").setField(inputMessage.fieldMap().get("name")).build();
117+
HttpBinding.builder()
118+
.setName("name")
119+
.setJsonName("name")
120+
.setField(inputMessage.fieldMap().get("name"))
121+
.build();
118122
HttpBinding expected2 =
119123
HttpBinding.builder()
120124
.setName("nested_object")
125+
.setJsonName("nestedObject")
121126
.setField(inputMessage.fieldMap().get("nested_object"))
122127
.build();
123128
Truth.assertThat(new HashSet<>(actual.queryParameters())).containsExactly(expected1, expected2);
@@ -173,4 +178,32 @@ void parseHttpAnnotation_shouldExcludeFieldsFromQueryParamsIfPathParamsAreConfig
173178
Truth.assertThat(new HashSet<>(actual.queryParameters())).containsExactly(expected1, expected2);
174179
Truth.assertThat(new HashSet<>(actual.pathParameters())).containsExactly(expectedPathParam);
175180
}
181+
182+
@Test
183+
void parseHttpAnnotation_respectsJsonNameWithDashes() {
184+
FileDescriptor complianceFileDescriptor =
185+
com.google.showcase.v1beta1.ComplianceOuterClass.getDescriptor();
186+
ServiceDescriptor complianceService = complianceFileDescriptor.getServices().get(0);
187+
assertEquals("Compliance", complianceService.getName());
188+
189+
Map<String, Message> messages = Parser.parseMessages(complianceFileDescriptor);
190+
191+
MethodDescriptor rpcMethod =
192+
complianceService.getMethods().stream()
193+
.filter(m -> m.getName().equals("RepeatDataQuery"))
194+
.findAny()
195+
.get();
196+
197+
Message inputMessage = messages.get("com.google.showcase.v1beta1.RepeatRequest");
198+
HttpBindings httpBindings = HttpRuleParser.parse(rpcMethod, inputMessage, messages);
199+
200+
HttpBinding customBinding =
201+
httpBindings.queryParameters().stream()
202+
.filter(b -> b.name().equals("custom_kebab_name"))
203+
.findAny()
204+
.orElse(null);
205+
206+
Truth.assertThat(customBinding).isNotNull();
207+
assertEquals("custom-kebab-name", customBinding.jsonName());
208+
}
176209
}

sdk-platform-java/gapic-generator-java/src/test/proto/compliance.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ message RepeatRequest {
127127
// If true, the server will verify that the received request matches
128128
// the request with the same name in the compliance test suite.
129129
bool server_verify = 3;
130+
131+
// New field for testing json_name with dashes
132+
string custom_kebab_name = 4 [json_name = "custom-kebab-name"];
130133
}
131134

132135
message RepeatResponse {

0 commit comments

Comments
 (0)