1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.api.generator.gapic.model;
16 
17 import static com.google.common.truth.Truth.assertThat;
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertTrue;
21 
22 import com.google.api.generator.gapic.protoparser.Parser;
23 import com.google.api.generator.gapic.protoparser.ServiceConfigParser;
24 import com.google.protobuf.Descriptors.FileDescriptor;
25 import com.google.protobuf.util.Durations;
26 import com.google.rpc.Code;
27 import com.google.showcase.v1beta1.EchoOuterClass;
28 import io.grpc.serviceconfig.MethodConfig;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.util.Arrays;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.Set;
37 import org.junit.Test;
38 
39 public class GapicServiceConfigTest {
40 
41   private static final double EPSILON = 1e-4;
42   private static final String TESTDATA_DIRECTORY = "src/test/resources/";
43 
44   @Test
serviceConfig_noConfigsFound()45   public void serviceConfig_noConfigsFound() {
46     FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor();
47     Service service = parseService(echoFileDescriptor);
48 
49     String jsonFilename = "retrying_grpc_service_config.json";
50     Path jsonPath = Paths.get(TESTDATA_DIRECTORY, jsonFilename);
51     Optional<GapicServiceConfig> serviceConfigOpt = ServiceConfigParser.parse(jsonPath.toString());
52     assertTrue(serviceConfigOpt.isPresent());
53     GapicServiceConfig serviceConfig = serviceConfigOpt.get();
54 
55     Map<String, GapicRetrySettings> retrySettings = serviceConfig.getAllGapicRetrySettings(service);
56     assertEquals(1, retrySettings.size());
57     String retryParamsName = serviceConfig.getRetryParamsName(service, service.methods().get(0));
58     assertEquals("no_retry_params", retryParamsName);
59 
60     assertEquals(GapicServiceConfig.EMPTY_TIMEOUT, retrySettings.get(retryParamsName).timeout());
61     assertEquals(
62         GapicServiceConfig.EMPTY_RETRY_POLICY, retrySettings.get(retryParamsName).retryPolicy());
63     assertEquals(GapicRetrySettings.Kind.NONE, retrySettings.get(retryParamsName).kind());
64 
65     Map<String, List<Code>> retryCodes = serviceConfig.getAllRetryCodes(service);
66     assertEquals(1, retryCodes.size());
67     assertEquals(
68         "no_retry_codes", serviceConfig.getRetryCodeName(service, service.methods().get(0)));
69     assertEquals(GapicServiceConfig.EMPTY_RETRY_CODES, retryCodes.get("no_retry_codes"));
70   }
71 
72   @Test
serviceConfig_basic()73   public void serviceConfig_basic() {
74     FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor();
75     Service service = parseService(echoFileDescriptor);
76 
77     String jsonFilename = "showcase_grpc_service_config.json";
78     Path jsonPath = Paths.get(TESTDATA_DIRECTORY, jsonFilename);
79     Optional<GapicServiceConfig> serviceConfigOpt = ServiceConfigParser.parse(jsonPath.toString());
80     assertTrue(serviceConfigOpt.isPresent());
81     GapicServiceConfig serviceConfig = serviceConfigOpt.get();
82 
83     Map<String, GapicRetrySettings> retrySettings = serviceConfig.getAllGapicRetrySettings(service);
84     assertEquals(2, retrySettings.size());
85     Map<String, List<Code>> retryCodes = serviceConfig.getAllRetryCodes(service);
86     assertEquals(2, retryCodes.size());
87 
88     // Echo method has an explicitly-defined setting.
89     Method method = findMethod(service, "Echo");
90     assertThat(method).isNotNull();
91     String retryParamsName = serviceConfig.getRetryParamsName(service, method);
92     assertEquals("retry_policy_1_params", retryParamsName);
93     GapicRetrySettings settings = retrySettings.get(retryParamsName);
94     assertThat(settings).isNotNull();
95     assertEquals(10, settings.timeout().getSeconds());
96     assertEquals(GapicRetrySettings.Kind.FULL, settings.kind());
97 
98     MethodConfig.RetryPolicy retryPolicy = settings.retryPolicy();
99     assertEquals(3, retryPolicy.getMaxAttempts());
100     assertEquals(100, Durations.toMillis(retryPolicy.getInitialBackoff()));
101     assertEquals(3000, Durations.toMillis(retryPolicy.getMaxBackoff()));
102     assertEquals(2.0, retryPolicy.getBackoffMultiplier(), EPSILON);
103 
104     String retryCodeName = serviceConfig.getRetryCodeName(service, method);
105     assertEquals("retry_policy_1_codes", retryCodeName);
106     List<Code> retryCode = retryCodes.get(retryCodeName);
107     assertThat(retryCode).containsExactly(Code.UNAVAILABLE, Code.UNKNOWN);
108 
109     // Chat method defaults to the service-defined setting.
110     method = findMethod(service, "Chat");
111     assertThat(method).isNotNull();
112     retryParamsName = serviceConfig.getRetryParamsName(service, method);
113     assertEquals("no_retry_0_params", retryParamsName);
114 
115     settings = retrySettings.get(retryParamsName);
116     assertThat(settings).isNotNull();
117     assertEquals(5, settings.timeout().getSeconds());
118     assertEquals(GapicServiceConfig.EMPTY_RETRY_POLICY, settings.retryPolicy());
119     assertEquals(GapicRetrySettings.Kind.NO_RETRY, settings.kind());
120 
121     retryCodeName = serviceConfig.getRetryCodeName(service, method);
122     assertEquals("no_retry_0_codes", retryCodeName);
123     assertEquals(GapicServiceConfig.EMPTY_RETRY_CODES, retryCodes.get(retryCodeName));
124   }
125 
126   @Test
serviceConfig_withBatchingSettings()127   public void serviceConfig_withBatchingSettings() {
128     FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor();
129     Service service = parseService(echoFileDescriptor);
130 
131     String jsonFilename = "showcase_grpc_service_config.json";
132     Path jsonPath = Paths.get(TESTDATA_DIRECTORY, jsonFilename);
133 
134     GapicBatchingSettings origBatchingSetting =
135         GapicBatchingSettings.builder()
136             .setProtoPakkage("google.showcase.v1beta1")
137             .setServiceName("Echo")
138             .setMethodName("Echo")
139             .setElementCountThreshold(1000)
140             .setRequestByteThreshold(2000)
141             .setDelayThresholdMillis(3000)
142             .setBatchedFieldName("name")
143             .setDiscriminatorFieldNames(Arrays.asList("severity"))
144             .build();
145     Optional<List<GapicBatchingSettings>> batchingSettingsOpt =
146         Optional.of(Arrays.asList(origBatchingSetting));
147 
148     Optional<GapicServiceConfig> serviceConfigOpt = ServiceConfigParser.parse(jsonPath.toString());
149     assertTrue(serviceConfigOpt.isPresent());
150     GapicServiceConfig serviceConfig = serviceConfigOpt.get();
151     serviceConfig.setBatchingSettings(batchingSettingsOpt);
152 
153     Map<String, GapicRetrySettings> retrySettings = serviceConfig.getAllGapicRetrySettings(service);
154     assertEquals(2, retrySettings.size());
155     Map<String, List<Code>> retryCodes = serviceConfig.getAllRetryCodes(service);
156     assertEquals(2, retryCodes.size());
157 
158     // Echo method has an explicitly-defined setting.
159     Method method = findMethod(service, "Echo");
160     assertThat(method).isNotNull();
161 
162     // No change to the retry settings.
163     String retryParamsName = serviceConfig.getRetryParamsName(service, method);
164     assertEquals("retry_policy_1_params", retryParamsName);
165     GapicRetrySettings settings = retrySettings.get(retryParamsName);
166     assertThat(settings).isNotNull();
167     assertEquals(10, settings.timeout().getSeconds());
168     assertEquals(GapicRetrySettings.Kind.FULL, settings.kind());
169 
170     // No changge to the retry codes.
171     String retryCodeName = serviceConfig.getRetryCodeName(service, method);
172     assertEquals("retry_policy_1_codes", retryCodeName);
173     List<Code> retryCode = retryCodes.get(retryCodeName);
174     assertThat(retryCode).containsExactly(Code.UNAVAILABLE, Code.UNKNOWN);
175 
176     // Check batching settings.
177     assertTrue(serviceConfig.hasBatchingSetting(service, method));
178     Optional<GapicBatchingSettings> batchingSettingOpt =
179         serviceConfig.getBatchingSetting(service, method);
180     assertTrue(batchingSettingOpt.isPresent());
181     GapicBatchingSettings batchingSetting = batchingSettingOpt.get();
182     assertEquals(
183         origBatchingSetting.elementCountThreshold(), batchingSetting.elementCountThreshold());
184     assertEquals(
185         origBatchingSetting.requestByteThreshold(), batchingSetting.requestByteThreshold());
186     assertEquals(
187         origBatchingSetting.delayThresholdMillis(), batchingSetting.delayThresholdMillis());
188 
189     // Chat method defaults to the service-defined setting.
190     method = findMethod(service, "Chat");
191     assertThat(method).isNotNull();
192     retryParamsName = serviceConfig.getRetryParamsName(service, method);
193     assertEquals("no_retry_0_params", retryParamsName);
194     retryCodeName = serviceConfig.getRetryCodeName(service, method);
195     assertEquals("no_retry_0_codes", retryCodeName);
196     assertFalse(serviceConfig.hasBatchingSetting(service, method));
197   }
198 
199   @Test
serviceConfig_withLroRetrySettings()200   public void serviceConfig_withLroRetrySettings() {
201     FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor();
202     Service service = parseService(echoFileDescriptor);
203 
204     String jsonFilename = "showcase_grpc_service_config.json";
205     Path jsonPath = Paths.get(TESTDATA_DIRECTORY, jsonFilename);
206 
207     // Construct LRO retry settings.
208     GapicLroRetrySettings origLroRetrySetting =
209         GapicLroRetrySettings.builder()
210             .setProtoPakkage("google.showcase.v1beta1")
211             .setServiceName("Echo")
212             .setMethodName("Echo")
213             .setInitialPollDelayMillis(100)
214             .setPollDelayMultiplier(1.5)
215             .setMaxPollDelayMillis(200)
216             .setTotalPollTimeoutMillis(300)
217             .build();
218     Optional<List<GapicLroRetrySettings>> lroRetrySettingsOpt =
219         Optional.of(Arrays.asList(origLroRetrySetting));
220 
221     Optional<GapicServiceConfig> serviceConfigOpt = ServiceConfigParser.parse(jsonPath.toString());
222     assertTrue(serviceConfigOpt.isPresent());
223     GapicServiceConfig serviceConfig = serviceConfigOpt.get();
224     serviceConfig.setLroRetrySettings(lroRetrySettingsOpt);
225 
226     // Check LRO retry settings.
227     Method method = findMethod(service, "Echo");
228     assertTrue(serviceConfig.hasLroRetrySetting(service, method));
229     Optional<GapicLroRetrySettings> retrievedSettingsOpt =
230         serviceConfig.getLroRetrySetting(service, method);
231     assertTrue(retrievedSettingsOpt.isPresent());
232     GapicLroRetrySettings retrievedSettings = retrievedSettingsOpt.get();
233     assertEquals(
234         origLroRetrySetting.initialPollDelayMillis(), retrievedSettings.initialPollDelayMillis());
235     assertEquals(origLroRetrySetting.maxPollDelayMillis(), retrievedSettings.maxPollDelayMillis());
236     assertEquals(
237         origLroRetrySetting.totalPollTimeoutMillis(), retrievedSettings.totalPollTimeoutMillis());
238   }
239 
parseService(FileDescriptor fileDescriptor)240   private static Service parseService(FileDescriptor fileDescriptor) {
241     Map<String, Message> messageTypes = Parser.parseMessages(fileDescriptor);
242     Map<String, ResourceName> resourceNames = Parser.parseResourceNames(fileDescriptor);
243     Set<ResourceName> outputResourceNames = new HashSet<>();
244     List<Service> services =
245         Parser.parseService(
246             fileDescriptor, messageTypes, resourceNames, Optional.empty(), outputResourceNames);
247     assertEquals(1, services.size());
248 
249     return services.get(0);
250   }
251 
findMethod(Service service, String methodName)252   private static Method findMethod(Service service, String methodName) {
253     for (Method m : service.methods()) {
254       if (m.name().equals(methodName)) {
255         return m;
256       }
257     }
258     return null;
259   }
260 }
261