Skip to content
This repository was archived by the owner on Jun 28, 2022. It is now read-only.

Commit c3097f0

Browse files
Bug fixes for gapic config v2 parsing (#2717)
* Fix null pointer exception when Api model is null * Correctly handle annotations plus v2 config case for interfaces
1 parent 70f22db commit c3097f0

File tree

4 files changed

+146
-37
lines changed

4 files changed

+146
-37
lines changed

src/main/java/com/google/api/codegen/config/GapicProductConfig.java

+54-28
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.api.tools.framework.model.Interface;
3636
import com.google.api.tools.framework.model.Method;
3737
import com.google.api.tools.framework.model.Model;
38+
import com.google.api.tools.framework.model.ProtoElement;
3839
import com.google.api.tools.framework.model.ProtoFile;
3940
import com.google.api.tools.framework.model.SimpleLocation;
4041
import com.google.api.tools.framework.model.SymbolTable;
@@ -47,17 +48,16 @@
4748
import com.google.common.collect.Iterables;
4849
import com.google.protobuf.Api;
4950
import com.google.protobuf.DescriptorProtos;
50-
import java.util.Collection;
5151
import java.util.Comparator;
5252
import java.util.HashMap;
5353
import java.util.HashSet;
5454
import java.util.LinkedHashMap;
5555
import java.util.List;
5656
import java.util.Map;
57-
import java.util.Map.Entry;
5857
import java.util.Objects;
5958
import java.util.Optional;
6059
import java.util.Set;
60+
import java.util.function.Function;
6161
import java.util.stream.Collectors;
6262
import javax.annotation.Nullable;
6363
import org.apache.commons.lang3.StringUtils;
@@ -279,16 +279,18 @@ public static GapicProductConfig create(
279279
getInterfacesFromProtoFile(diagCollector, sourceProtos, symbolTable);
280280

281281
ImmutableList<GapicInterfaceInput> interfaceInputs;
282-
if (!configProto.equals(ConfigProto.getDefaultInstance())) {
282+
if (protoParser.isProtoAnnotationsEnabled()) {
283+
interfaceInputs =
284+
createInterfaceInputsWithAnnotationsAndGapicConfig(
285+
diagCollector, configProto.getInterfacesList(), protoInterfaces, language);
286+
} else {
283287
interfaceInputs =
284-
createInterfaceInputsWithGapicConfig(
288+
createInterfaceInputsWithGapicConfigOnly(
285289
diagCollector,
286290
configProto.getInterfacesList(),
287291
protoInterfaces,
288292
symbolTable,
289293
language);
290-
} else {
291-
interfaceInputs = createInterfaceInputsWithoutGapicConfig(protoInterfaces.values());
292294
}
293295
if (interfaceInputs == null) {
294296
return null;
@@ -475,7 +477,22 @@ private static GapicProductConfig createDummyInstance(
475477
}
476478

477479
/** Return the list of information about clients to be generated. */
478-
private static ImmutableList<GapicInterfaceInput> createInterfaceInputsWithGapicConfig(
480+
private static ImmutableList<GapicInterfaceInput>
481+
createInterfaceInputsWithAnnotationsAndGapicConfig(
482+
DiagCollector diagCollector,
483+
List<InterfaceConfigProto> interfaceConfigProtosList,
484+
ImmutableMap<String, Interface> protoInterfaces,
485+
TargetLanguage language) {
486+
return createGapicInterfaceInputList(
487+
diagCollector,
488+
language,
489+
protoInterfaces.values(),
490+
interfaceConfigProtosList
491+
.stream()
492+
.collect(Collectors.toMap(InterfaceConfigProto::getName, Function.identity())));
493+
}
494+
495+
private static ImmutableList<GapicInterfaceInput> createInterfaceInputsWithGapicConfigOnly(
479496
DiagCollector diagCollector,
480497
List<InterfaceConfigProto> interfaceConfigProtosList,
481498
ImmutableMap<String, Interface> protoInterfaces,
@@ -491,23 +508,48 @@ private static ImmutableList<GapicInterfaceInput> createInterfaceInputsWithGapic
491508
// Parse GAPIC config for interfaceConfigProtos.
492509
for (InterfaceConfigProto interfaceConfigProto : interfaceConfigProtosList) {
493510
Interface apiInterface = symbolTable.lookupInterface(interfaceConfigProto.getName());
494-
if (apiInterface == null || !apiInterface.isReachable()) {
511+
if (apiInterface == null) {
512+
List<String> interfaces =
513+
symbolTable
514+
.getInterfaces()
515+
.stream()
516+
.map(ProtoElement::getFullName)
517+
.collect(Collectors.toList());
518+
String interfacesString = String.join(",", interfaces);
519+
diagCollector.addDiag(
520+
Diag.error(
521+
SimpleLocation.TOPLEVEL,
522+
"interface not found: %s. Interfaces: [%s]",
523+
interfaceConfigProto.getName(),
524+
interfacesString));
525+
continue;
526+
}
527+
if (!apiInterface.isReachable()) {
495528
diagCollector.addDiag(
496529
Diag.error(
497530
SimpleLocation.TOPLEVEL,
498-
"interface not found: %s",
531+
"interface not reachable: %s.",
499532
interfaceConfigProto.getName()));
500533
continue;
501534
}
502535
interfaceConfigProtos.put(interfaceConfigProto.getName(), interfaceConfigProto);
503536
interfaceMap.put(interfaceConfigProto.getName(), apiInterface);
504537
}
505538

539+
return createGapicInterfaceInputList(
540+
diagCollector, language, interfaceMap.values(), interfaceConfigProtos);
541+
}
542+
543+
private static ImmutableList<GapicInterfaceInput> createGapicInterfaceInputList(
544+
DiagCollector diagCollector,
545+
TargetLanguage language,
546+
Iterable<Interface> interfaceList,
547+
Map<String, InterfaceConfigProto> interfaceConfigProtos) {
548+
506549
// Store info about each Interface in a GapicInterfaceInput object.
507550
ImmutableList.Builder<GapicInterfaceInput> interfaceInputs = ImmutableList.builder();
508-
for (Entry<String, Interface> interfaceEntry : interfaceMap.entrySet()) {
509-
String serviceFullName = interfaceEntry.getKey();
510-
Interface apiInterface = interfaceEntry.getValue();
551+
for (Interface apiInterface : interfaceList) {
552+
String serviceFullName = apiInterface.getFullName();
511553
GapicInterfaceInput.Builder interfaceInput =
512554
GapicInterfaceInput.newBuilder().setInterface(apiInterface);
513555

@@ -552,22 +594,6 @@ private static ImmutableMap<String, Interface> getInterfacesFromProtoFile(
552594
return protoInterfaces.build();
553595
}
554596

555-
/** Return the list of information about clients to be generated. */
556-
private static ImmutableList<GapicInterfaceInput> createInterfaceInputsWithoutGapicConfig(
557-
Collection<Interface> protoInterfaces) {
558-
// Store info about each Interface in a GapicInterfaceInput object.
559-
ImmutableList.Builder<GapicInterfaceInput> interfaceInputs = ImmutableList.builder();
560-
for (Interface apiInterface : protoInterfaces) {
561-
GapicInterfaceInput.Builder interfaceInput =
562-
GapicInterfaceInput.newBuilder()
563-
.setInterface(apiInterface)
564-
.setInterfaceConfigProto(InterfaceConfigProto.getDefaultInstance())
565-
.setMethodsToGenerate(findMethodsToGenerateWithoutConfigYaml(apiInterface));
566-
interfaceInputs.add(interfaceInput.build());
567-
}
568-
return interfaceInputs.build();
569-
}
570-
571597
/** Find the methods that should be generated on the surface when no GAPIC config was given. */
572598
private static ImmutableMap<Method, MethodConfigProto> findMethodsToGenerateWithoutConfigYaml(
573599
Interface apiInterface) {

src/main/java/com/google/api/codegen/config/ProtoInterfaceModel.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.google.api.tools.framework.model.Method;
1919
import com.google.api.tools.framework.model.SymbolTable;
2020
import com.google.common.collect.ImmutableList;
21+
import com.google.protobuf.Api;
2122
import com.google.protobuf.Mixin;
2223
import java.util.List;
2324

@@ -73,10 +74,13 @@ public List<MethodModel> getMethods() {
7374
methods.add(new ProtoMethodModel(method));
7475
}
7576
SymbolTable symbolTable = protoInterface.getModel().getSymbolTable();
76-
for (Mixin mixin : protoInterface.getConfig().getMixinsList()) {
77-
Interface mixinInterface = symbolTable.lookupInterface(mixin.getName());
78-
for (Method method : mixinInterface.getMethods()) {
79-
methods.add(new ProtoMethodModel(method));
77+
Api protoInterfaceConfig = protoInterface.getConfig();
78+
if (protoInterfaceConfig != null) {
79+
for (Mixin mixin : protoInterface.getConfig().getMixinsList()) {
80+
Interface mixinInterface = symbolTable.lookupInterface(mixin.getName());
81+
for (Method method : mixinInterface.getMethods()) {
82+
methods.add(new ProtoMethodModel(method));
83+
}
8084
}
8185
}
8286
return methods.build();

src/test/java/com/google/api/codegen/config/GapicConfigProducerTest.java

+24-5
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,11 @@ public class GapicConfigProducerTest {
3232

3333
@ClassRule public static TemporaryFolder tempDir = new TemporaryFolder();
3434

35-
private static Model model;
36-
private static GapicProductConfig productConfig;
37-
3835
@Test
3936
public void missingConfigSchemaVersion() {
4037
TestDataLocator locator = MixedPathTestDataLocator.create(this.getClass());
4138
locator.addTestDataSource(CodegenTestUtil.class, "testsrc/common");
42-
model =
39+
Model model =
4340
CodegenTestUtil.readModel(
4441
locator, tempDir, new String[] {"myproto.proto"}, new String[] {"myproto.yaml"});
4542

@@ -48,11 +45,33 @@ public void missingConfigSchemaVersion() {
4845
model.getDiagReporter().getDiagCollector(),
4946
locator,
5047
new String[] {"missing_config_schema_version.yaml"});
51-
productConfig = GapicProductConfig.create(model, configProto, null, null, TargetLanguage.JAVA);
48+
GapicProductConfig.create(model, configProto, null, null, TargetLanguage.JAVA);
5249
Diag expectedError =
5350
Diag.error(
5451
SimpleLocation.TOPLEVEL, "config_schema_version field is required in GAPIC yaml.");
5552
assertThat(model.getDiagReporter().getDiagCollector().hasErrors()).isTrue();
5653
assertThat(model.getDiagReporter().getDiagCollector().getDiags()).contains(expectedError);
5754
}
55+
56+
@Test
57+
public void missingInterface() {
58+
TestDataLocator locator = MixedPathTestDataLocator.create(this.getClass());
59+
locator.addTestDataSource(CodegenTestUtil.class, "testsrc/common");
60+
Model model =
61+
CodegenTestUtil.readModel(
62+
locator, tempDir, new String[] {"myproto.proto"}, new String[] {"myproto.yaml"});
63+
64+
ConfigProto configProto =
65+
CodegenTestUtil.readConfig(
66+
model.getDiagReporter().getDiagCollector(),
67+
locator,
68+
new String[] {"missing_interface_v1.yaml"});
69+
GapicProductConfig.create(model, configProto, null, null, TargetLanguage.JAVA);
70+
Diag expectedError =
71+
Diag.error(
72+
SimpleLocation.TOPLEVEL,
73+
"interface not found: google.example.myproto.v1.MyUnknownProto. Interfaces: [google.example.myproto.v1.MyProto]");
74+
assertThat(model.getDiagReporter().getDiagCollector().hasErrors()).isTrue();
75+
assertThat(model.getDiagReporter().getDiagCollector().getDiags()).contains(expectedError);
76+
}
5877
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
type: com.google.api.codegen.ConfigProto
2+
config_schema_version: 1.0.0
3+
language_settings:
4+
go:
5+
package_name: cloud.google.com/go/myapi/apiv1
6+
interfaces:
7+
- name: google.example.myproto.v1.MyProto
8+
retry_codes_def:
9+
- name: idempotent
10+
retry_codes:
11+
- DEADLINE_EXCEEDED
12+
- name: non_idempotent
13+
retry_codes:
14+
retry_params_def:
15+
- name: default
16+
initial_retry_delay_millis: 100
17+
retry_delay_multiplier: 1.2
18+
max_retry_delay_millis: 1000
19+
initial_rpc_timeout_millis: 300
20+
rpc_timeout_multiplier: 1.3
21+
max_rpc_timeout_millis: 3000
22+
total_timeout_millis: 30000
23+
methods:
24+
- name: MyMethod
25+
flattening:
26+
groups:
27+
- parameters:
28+
- myfield
29+
required_fields:
30+
- myfield
31+
retry_codes_name: non_idempotent
32+
retry_params_name: default
33+
timeout_millis: 1000
34+
- name: google.example.myproto.v1.MyUnknownProto
35+
retry_codes_def:
36+
- name: idempotent
37+
retry_codes:
38+
- DEADLINE_EXCEEDED
39+
- name: non_idempotent
40+
retry_codes:
41+
retry_params_def:
42+
- name: default
43+
initial_retry_delay_millis: 100
44+
retry_delay_multiplier: 1.2
45+
max_retry_delay_millis: 1000
46+
initial_rpc_timeout_millis: 300
47+
rpc_timeout_multiplier: 1.3
48+
max_rpc_timeout_millis: 3000
49+
total_timeout_millis: 30000
50+
methods:
51+
- name: MyMethod
52+
flattening:
53+
groups:
54+
- parameters:
55+
- myfield
56+
required_fields:
57+
- myfield
58+
retry_codes_name: non_idempotent
59+
retry_params_name: default
60+
timeout_millis: 1000

0 commit comments

Comments
 (0)