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