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.protoparser;
16 
17 import com.google.api.generator.gapic.model.GapicLroRetrySettings;
18 import com.google.common.annotations.VisibleForTesting;
19 import com.google.common.base.Strings;
20 import java.io.File;
21 import java.io.IOException;
22 import java.nio.charset.StandardCharsets;
23 import java.nio.file.Files;
24 import java.nio.file.Paths;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Optional;
29 import org.yaml.snakeyaml.LoaderOptions;
30 import org.yaml.snakeyaml.Yaml;
31 import org.yaml.snakeyaml.constructor.SafeConstructor;
32 
33 public class GapicLroRetrySettingsParser {
34   private static final String YAML_KEY_INTERFACES = "interfaces";
35   private static final String YAML_KEY_NAME = "name";
36   private static final String YAML_KEY_METHODS = "methods";
37   private static final String YAML_KEY_LONG_RUNNING = "long_running";
38 
39   private static final String YAML_KEY_INITIAL_POLL_DELAY_MILLIS = "initial_poll_delay_millis";
40   private static final String YAML_KEY_POLL_DELAY_MULTIPLIER = "poll_delay_multiplier";
41   private static final String YAML_KEY_MAX_POLL_DELAY_MILLIS = "max_poll_delay_millis";
42   private static final String YAML_KEY_TOTAL_POLL_TIMEOUT_MILLIS = "total_poll_timeout_millis";
43 
parse( Optional<String> gapicYamlConfigFilePathOpt)44   public static Optional<List<GapicLroRetrySettings>> parse(
45       Optional<String> gapicYamlConfigFilePathOpt) {
46     return gapicYamlConfigFilePathOpt.isPresent()
47         ? parse(gapicYamlConfigFilePathOpt.get())
48         : Optional.empty();
49   }
50 
51   @VisibleForTesting
parse(String gapicYamlConfigFilePath)52   static Optional<List<GapicLroRetrySettings>> parse(String gapicYamlConfigFilePath) {
53     if (Strings.isNullOrEmpty(gapicYamlConfigFilePath)
54         || !(new File(gapicYamlConfigFilePath)).exists()) {
55       return Optional.empty();
56     }
57 
58     String fileContents = null;
59 
60     try {
61       fileContents =
62           new String(
63               Files.readAllBytes(Paths.get(gapicYamlConfigFilePath)), StandardCharsets.UTF_8);
64     } catch (IOException e) {
65       return Optional.empty();
66     }
67 
68     Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
69     Map<String, Object> yamlMap = yaml.load(fileContents);
70     return parseFromMap(yamlMap);
71   }
72 
parseFromMap(Map<String, Object> yamlMap)73   private static Optional<List<GapicLroRetrySettings>> parseFromMap(Map<String, Object> yamlMap) {
74     if (!yamlMap.containsKey(YAML_KEY_INTERFACES)) {
75       return Optional.empty();
76     }
77 
78     List<GapicLroRetrySettings> settings = new ArrayList<>();
79     for (Map<String, Object> serviceYamlConfig :
80         (List<Map<String, Object>>) yamlMap.get(YAML_KEY_INTERFACES)) {
81       if (!serviceYamlConfig.containsKey(YAML_KEY_METHODS)) {
82         continue;
83       }
84       for (Map<String, Object> methodYamlConfig :
85           (List<Map<String, Object>>) serviceYamlConfig.get(YAML_KEY_METHODS)) {
86         if (!methodYamlConfig.containsKey(YAML_KEY_LONG_RUNNING)) {
87           continue;
88         }
89 
90         Map<String, Object> lroRetrySettingsYamlConfig =
91             (Map<String, Object>) methodYamlConfig.get(YAML_KEY_LONG_RUNNING);
92         if (!lroRetrySettingsYamlConfig.containsKey(YAML_KEY_INITIAL_POLL_DELAY_MILLIS)
93             || !lroRetrySettingsYamlConfig.containsKey(YAML_KEY_POLL_DELAY_MULTIPLIER)
94             || !lroRetrySettingsYamlConfig.containsKey(YAML_KEY_MAX_POLL_DELAY_MILLIS)
95             || !lroRetrySettingsYamlConfig.containsKey(YAML_KEY_TOTAL_POLL_TIMEOUT_MILLIS)) {
96           continue;
97         }
98 
99         String interfaceName = (String) serviceYamlConfig.get(YAML_KEY_NAME);
100         int lastDotIndex = interfaceName.lastIndexOf(".");
101         String protoPakkage = interfaceName.substring(0, lastDotIndex);
102         String serviceName = interfaceName.substring(lastDotIndex + 1);
103         String methodName = (String) methodYamlConfig.get(YAML_KEY_NAME);
104 
105         GapicLroRetrySettings.Builder lroRetrySettingsBuilder =
106             GapicLroRetrySettings.builder()
107                 .setProtoPakkage(protoPakkage)
108                 .setServiceName(serviceName)
109                 .setMethodName(methodName)
110                 .setInitialPollDelayMillis(
111                     (Integer) lroRetrySettingsYamlConfig.get(YAML_KEY_INITIAL_POLL_DELAY_MILLIS))
112                 .setMaxPollDelayMillis(
113                     (Integer) lroRetrySettingsYamlConfig.get(YAML_KEY_MAX_POLL_DELAY_MILLIS))
114                 .setTotalPollTimeoutMillis(
115                     (Integer) lroRetrySettingsYamlConfig.get(YAML_KEY_TOTAL_POLL_TIMEOUT_MILLIS));
116 
117         // Workaround for snakeyaml's automatic type inference. When given a value like "1",
118         // snakeyaml interprets this as an integer, whereas a value like "1.5" is interpreted as
119         // a double.
120         Object pollDelayMultObj = lroRetrySettingsYamlConfig.get(YAML_KEY_POLL_DELAY_MULTIPLIER);
121         if (pollDelayMultObj instanceof Integer) {
122           lroRetrySettingsBuilder =
123               lroRetrySettingsBuilder.setPollDelayMultiplier((Integer) pollDelayMultObj);
124         } else if (pollDelayMultObj instanceof Double) {
125           lroRetrySettingsBuilder =
126               lroRetrySettingsBuilder.setPollDelayMultiplier((Double) pollDelayMultObj);
127         }
128         settings.add(lroRetrySettingsBuilder.build());
129       }
130     }
131     return settings.isEmpty() ? Optional.empty() : Optional.of(settings);
132   }
133 }
134