java/com/google/cloud/deploymentmanager/autogen/SoyFunctions.java (776 lines of code) (raw):

// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.cloud.deploymentmanager.autogen; import static com.google.cloud.deploymentmanager.autogen.proto.GceMetadataItem.ValueSpecCase.VALUE_FROM_DEPLOY_INPUT_FIELD; import static com.google.cloud.deploymentmanager.autogen.proto.LocalSsdSpec.CountSpecCase.COUNT_FROM_DEPLOY_INPUT_FIELD; import static com.google.template.soy.data.SoyValueConverter.markAsSoyMap; import com.google.cloud.deploymentmanager.autogen.proto.BooleanExpression; import com.google.cloud.deploymentmanager.autogen.proto.BooleanExpression.BooleanDeployInputField; import com.google.cloud.deploymentmanager.autogen.proto.BooleanExpression.ExternalIpAvailability; import com.google.cloud.deploymentmanager.autogen.proto.DeployInputField; import com.google.cloud.deploymentmanager.autogen.proto.DeployInputSection; import com.google.cloud.deploymentmanager.autogen.proto.DeployInputSpec; import com.google.cloud.deploymentmanager.autogen.proto.DiskSpec; import com.google.cloud.deploymentmanager.autogen.proto.DiskSpec.DeviceName.DeviceNameCase; import com.google.cloud.deploymentmanager.autogen.proto.ExternalIpSpec; import com.google.cloud.deploymentmanager.autogen.proto.ExternalIpSpec.Type; import com.google.cloud.deploymentmanager.autogen.proto.GceMetadataItem; import com.google.cloud.deploymentmanager.autogen.proto.MultiVmDeploymentPackageSpec; import com.google.cloud.deploymentmanager.autogen.proto.SingleVmDeploymentPackageSpec; import com.google.cloud.deploymentmanager.autogen.proto.VmTierSpec; import com.google.cloud.deploymentmanager.autogen.proto.ZoneSpec; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.multibindings.Multibinder; import com.google.protobuf.Message; import com.google.template.soy.data.SoyList; import com.google.template.soy.data.SoyProtoValue; import com.google.template.soy.data.SoyValue; import com.google.template.soy.data.SoyValueConverter; import com.google.template.soy.data.restricted.NullData; import com.google.template.soy.data.restricted.StringData; import com.google.template.soy.shared.restricted.Signature; import com.google.template.soy.shared.restricted.SoyFunction; import com.google.template.soy.shared.restricted.SoyFunctionSignature; import com.google.template.soy.shared.restricted.SoyJavaFunction; import com.google.template.soy.shared.restricted.TypedSoyFunction; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * Additional soy functions to support our templating. */ final class SoyFunctions { static final class Module extends AbstractModule { @Override protected void configure() { Multibinder<SoyFunction> soyFunctionSetBinder = Multibinder.newSetBinder(binder(), SoyFunction.class); soyFunctionSetBinder.addBinding().to(DependentTiers.class); soyFunctionSetBinder.addBinding().to(DeployInputFieldIsString.class); soyFunctionSetBinder.addBinding().to(DeployInputFieldName.class); soyFunctionSetBinder.addBinding().to(FindDeployInputField.class); soyFunctionSetBinder.addBinding().to(FindInputsWithTestDefaultValues.class); soyFunctionSetBinder.addBinding().to(FindInputTestDefaultValue.class); soyFunctionSetBinder.addBinding().to(FindDisplayGroup.class); soyFunctionSetBinder.addBinding().to(FindVmTier.class); soyFunctionSetBinder.addBinding().to(FieldValueLabelMap.class); soyFunctionSetBinder.addBinding().to(TierPrefixed.class); soyFunctionSetBinder.addBinding().to(TierTemplateName.class); soyFunctionSetBinder.addBinding().to(BooleanExpressionDisplayCondition.class); soyFunctionSetBinder.addBinding().to(BooleanExpressionJinjaExpression.class); soyFunctionSetBinder.addBinding().to(AdditionalDiskTypePropertyName.class); soyFunctionSetBinder.addBinding().to(AdditionalDiskSizePropertyName.class); soyFunctionSetBinder.addBinding().to(ListDeployInputFields.class); soyFunctionSetBinder.addBinding().to(ExternalIpTypeName.class); soyFunctionSetBinder.addBinding().to(SolutionHasGpus.class); soyFunctionSetBinder.addBinding().to(GetTestConfigDefaultValues.class); } } /** * Returns all a tiers that the tier specified in the first argument depends upon. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "dependentTiers", value = { @Signature( parameterTypes = { "cloud.deploymentmanager.autogen.VmTierSpec", "cloud.deploymentmanager.autogen.MultiVmDeploymentPackageSpec" }, returnType = "list<cloud.deploymentmanager.autogen.VmTierSpec>") }) static final class DependentTiers extends TypedSoyFunction implements SoyJavaFunction { @Inject DependentTiers() {} @Override public SoyValue computeForJava(List<SoyValue> args) { VmTierSpec tier = (VmTierSpec) ((SoyProtoValue) args.get(0)).getProto(); List<VmTierSpec> tierList; try { tierList = extractTierList(args.get(1)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unexpected 2nd arg type for dependentTiers", e); } Set<Integer> dependentIndices = new HashSet<>(); for (GceMetadataItem item : tier.getGceMetadataItemsList()) { if (item.hasTierVmNames()) { dependentIndices.add(findTier(item.getTierVmNames().getTier(), tierList)); } } // List dependents in the order they appear in the tier list. List<VmTierSpec> dependents = new ArrayList<>(dependentIndices.size()); for (int i = 0; i < tierList.size(); i++) { if (dependentIndices.contains(i)) { dependents.add(tierList.get(i)); } } return SoyValueConverter.INSTANCE.convert(dependents).resolve(); } private int findTier(String name, List<VmTierSpec> tierList) { for (int i = 0; i < tierList.size(); i++) { if (name.equals(tierList.get(i).getName())) { return i; } } throw new IllegalArgumentException("Unable to find tier with name: " + name); } } @Singleton @SoyFunctionSignature( name = "deployInputFieldIsString", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.DeployInputField"}, returnType = "bool") }) private static final class DeployInputFieldIsString extends TypedSoyFunction implements SoyJavaFunction { @Inject DeployInputFieldIsString() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputField field = (DeployInputField) ((SoyProtoValue) args.get(0)).getProto(); switch (field.getTypeCase()) { case STRING_BOX: case EMAIL_BOX: case STRING_DROPDOWN: case ZONE_DROPDOWN: return SoyValueConverter.INSTANCE.convert(true).resolve(); default: return SoyValueConverter.INSTANCE.convert(false).resolve(); } } } /** * Derives the name for a deploy input field. * * <p>While we can take a string instead, forcing a proper field definition prevents the mistake * of entering an invalid field name. {@link FindDeployInputField} can be used to look up the * field definition from a field name, which will ensure that any invalid field name will fail. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "deployInputFieldName", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.DeployInputField"}, returnType = "string") }) static final class DeployInputFieldName extends TypedSoyFunction implements SoyJavaFunction { @Inject DeployInputFieldName() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputField field = (DeployInputField) ((SoyProtoValue) args.get(0)).getProto(); return StringData.forValue(formatFieldName(field.getName())); } public static String formatFieldName(String fieldSpecName) { return String.format("input_%s", fieldSpecName); } } /** Finds the input field definition from the field name. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "findDeployInputField", value = { @Signature( parameterTypes = { "string", "cloud.deploymentmanager.autogen.DeployInputSpec|null|undefined" }, returnType = "cloud.deploymentmanager.autogen.DeployInputField") }) static final class FindDeployInputField extends TypedSoyFunction implements SoyJavaFunction { @Inject FindDeployInputField() {} @Override public SoyValue computeForJava(List<SoyValue> list) { String name = list.get(0).stringValue(); DeployInputSpec deployInputSpec = (DeployInputSpec) ((SoyProtoValue) list.get(1)).getProto(); for (DeployInputSection section : deployInputSpec.getSectionsList()) { for (DeployInputField field : section.getFieldsList()) { if (name.equals(field.getName())) { return SoyValueConverter.INSTANCE.convert(field).resolve(); } } } throw new IllegalArgumentException("Unable to find deploy input field named: " + name); } } /** * Finds the collection of required input fields that have no defaultValue. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "findInputsWithTestDefaultValues", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.DeployInputSpec"}, returnType = "list<cloud.deploymentmanager.autogen.DeployInputField>") }) static final class FindInputsWithTestDefaultValues extends TypedSoyFunction implements SoyJavaFunction { @Inject FindInputsWithTestDefaultValues() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputSpec deployInputSpec = (DeployInputSpec) ((SoyProtoValue) args.get(0)).getProto(); List<DeployInputField> filteredFields = new ArrayList<>(); for (DeployInputSection section : deployInputSpec.getSectionsList()) { for (DeployInputField field : section.getFieldsList()) { if (hasTestDefaultValue(field)) { filteredFields.add(field); } } } return SoyValueConverter.INSTANCE.convert(filteredFields).resolve(); } private boolean hasTestDefaultValue(DeployInputField field) { switch (field.getTypeCase()) { case STRING_BOX: return !Strings.isNullOrEmpty(field.getStringBox().getTestDefaultValue()); case EMAIL_BOX: return !field.getEmailBox().getTestDefaultValue().isEmpty(); case INTEGER_BOX: return field.getIntegerBox().hasTestDefaultValue(); default: return false; } } } /** * Finds the default test value for given input field. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "findInputTestDefaultValue", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.DeployInputField"}, returnType = "string|int") }) static final class FindInputTestDefaultValue extends TypedSoyFunction implements SoyJavaFunction { @Inject FindInputTestDefaultValue() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputField field = (DeployInputField) ((SoyProtoValue) args.get(0)).getProto(); Object value; switch (field.getTypeCase()) { case STRING_BOX: value = field.getStringBox().getTestDefaultValue(); break; case EMAIL_BOX: value = field.getEmailBox().getTestDefaultValue(); break; case INTEGER_BOX: value = field.getIntegerBox().getTestDefaultValue().getValue(); break; default: throw new IllegalArgumentException( String.format( "Unexpected field type '%s' for field '%s')", field.getTypeCase(), field.getName())); } return SoyValueConverter.INSTANCE.convert(value).resolve(); } } @VisibleForTesting @Singleton @SoyFunctionSignature( name = "getTestConfigDefaultValues", value = { @Signature( parameterTypes = { "cloud.deploymentmanager.autogen.MultiVmDeploymentPackageSpec|cloud.deploymentmanager.autogen.SingleVmDeploymentPackageSpec" }, returnType = "map<string, string>") }) static final class GetTestConfigDefaultValues extends TypedSoyFunction implements SoyJavaFunction { @Inject GetTestConfigDefaultValues() {} private static final String DEFAULT_ZONE_PROP_NAME = "zone"; private static final String DEFAULT_ZONE = "us-central1-f"; private static Map<String, String> getZoneDefaultValue(ZoneSpec zoneSpec) { ImmutableMap.Builder<String, String> result = ImmutableMap.builder(); if (zoneSpec.getDefaultZone().isEmpty()) { result.put(DEFAULT_ZONE_PROP_NAME, DEFAULT_ZONE); } return result.buildOrThrow(); } @Override public SoyValue computeForJava(List<SoyValue> args) { Message message = ((SoyProtoValue) args.get(0)).getProto(); if (message instanceof SingleVmDeploymentPackageSpec) { return SoyValueConverter.INSTANCE .convert( markAsSoyMap( getZoneDefaultValue(((SingleVmDeploymentPackageSpec) message).getZone()))) .resolve(); } return SoyValueConverter.INSTANCE .convert( markAsSoyMap(getZoneDefaultValue(((MultiVmDeploymentPackageSpec) message).getZone()))) .resolve(); } } /** Finds the display group for a grouped boolean checkbox field. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "findDisplayGroup", value = { @Signature( parameterTypes = { "cloud.deploymentmanager.autogen.DeployInputField", "cloud.deploymentmanager.autogen.DeployInputSection" }, returnType = "cloud.deploymentmanager.autogen.DeployInputField.GroupedBooleanCheckbox.DisplayGroup") }) static final class FindDisplayGroup extends TypedSoyFunction implements SoyJavaFunction { @Inject FindDisplayGroup() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputField targetField = (DeployInputField) ((SoyProtoValue) args.get(0)).getProto(); Preconditions.checkArgument(targetField.hasGroupedBooleanCheckbox()); DeployInputSection section = (DeployInputSection) ((SoyProtoValue) args.get(1)).getProto(); boolean foundField = false; for (DeployInputField field : Lists.reverse(section.getFieldsList())) { if (!foundField) { if (targetField.getName().equals(field.getName())) { foundField = true; } } if (foundField) { if (!field.hasGroupedBooleanCheckbox()) { throw new RuntimeException( "No preceding grouped boolean checkbox field with a display group"); } if (field.getGroupedBooleanCheckbox().hasDisplayGroup()) { return SoyValueConverter.INSTANCE .convert(field.getGroupedBooleanCheckbox().getDisplayGroup()) .resolve(); } } } throw new RuntimeException( "Unable to locate display group for field " + targetField.getName()); } } /** Finds the corresponding {@code VmTierSpec} given its name. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "findVmTier", value = { @Signature( parameterTypes = { "string", "list<cloud.deploymentmanager.autogen.VmTierSpec>|null|undefined" }, returnType = "cloud.deploymentmanager.autogen.VmTierSpec") }) static final class FindVmTier extends TypedSoyFunction implements SoyJavaFunction { @Inject FindVmTier() {} @Override public SoyValue computeForJava(List<SoyValue> args) { String tierName = args.get(0).stringValue(); List<VmTierSpec> tierList; try { tierList = extractTierList(args.get(1)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unexpected 2nd arg type for findVmTier function", e); } for (VmTierSpec tier : tierList) { if (tierName.equals(tier.getName())) { return SoyValueConverter.INSTANCE.convert(tier).resolve(); } } throw new RuntimeException("Unable to locate tier with name " + tierName); } } /** Extracts the map of value labels for a deploy input field if any. */ @Singleton @SoyFunctionSignature( name = "fieldValueLabelMap", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.DeployInputField"}, returnType = "map<string, string>") }) private static final class FieldValueLabelMap extends TypedSoyFunction implements SoyJavaFunction { @Inject FieldValueLabelMap() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputField field = (DeployInputField) ((SoyProtoValue) args.get(0)).getProto(); if (field.hasIntegerDropdown()) { if (!field.getIntegerDropdown().getValueLabelsMap().isEmpty()) { return SoyValueConverter.INSTANCE .convert(toStringMap(field.getIntegerDropdown().getValueLabelsMap())) .resolve(); } } else if (field.hasStringDropdown()) { if (!field.getStringDropdown().getValueLabelsMap().isEmpty()) { return SoyValueConverter.INSTANCE .convert(field.getStringDropdown().getValueLabelsMap()) .resolve(); } } return NullData.INSTANCE; } private <K, V> ImmutableMap<String, String> toStringMap(Map<K, V> from) { ImmutableMap.Builder<String, String> result = ImmutableMap.builder(); for (Map.Entry<K, V> entry : from.entrySet()) { result.put(entry.getKey().toString(), entry.getValue().toString()); } return result.buildOrThrow(); } } /** Prefixes a string with a tier name. */ @VisibleForTesting @Singleton @SoyFunctionSignature( name = "tierPrefixed", value = { @Signature( parameterTypes = { "string", "cloud.deploymentmanager.autogen.VmTierSpec|null|undefined" }, returnType = "string"), @Signature( parameterTypes = { "string", "cloud.deploymentmanager.autogen.VmTierSpec|null|undefined", "string" }, returnType = "string") }) static final class TierPrefixed extends TypedSoyFunction implements SoyJavaFunction { @Inject TierPrefixed(SoyDirectives.TierPrefixed directive) { this.directive = directive; } SoyDirectives.TierPrefixed directive; @Override public SoyValue computeForJava(List<SoyValue> args) { return directive.applyForJava(args.get(0), args.subList(1, args.size())); } } @Singleton @SoyFunctionSignature( name = "tierTemplateName", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.VmTierSpec"}, returnType = "string") }) static final class TierTemplateName extends TypedSoyFunction implements SoyJavaFunction { @Inject TierTemplateName() {} @Override public SoyValue computeForJava(List<SoyValue> args) { VmTierSpec spec = (VmTierSpec) ((SoyProtoValue) args.get(0)).getProto(); return StringData.forValue(apply(spec)); } public static String apply(VmTierSpec spec) { return String.format("%s_tier.jinja", spec.getName()); } } @Singleton @SoyFunctionSignature( name = "booleanExpressionDisplayCondition", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.BooleanExpression"}, returnType = "string"), @Signature( parameterTypes = { "cloud.deploymentmanager.autogen.BooleanExpression", "list<cloud.deploymentmanager.autogen.VmTierSpec>|null|undefined" }, returnType = "string") }) static final class BooleanExpressionDisplayCondition extends TypedSoyFunction implements SoyJavaFunction { private final SoyFunctions.TierPrefixed tierPrefixedFunction; private final SoyFunctions.FindVmTier findVmTierFunction; private static final String AND = "and"; @Inject BooleanExpressionDisplayCondition(SoyFunctions.TierPrefixed tierPrefixedFunction, SoyFunctions.FindVmTier findVmTierFunction) { this.tierPrefixedFunction = tierPrefixedFunction; this.findVmTierFunction = findVmTierFunction; } @Override public SoyValue computeForJava(List<SoyValue> args) { BooleanExpression spec = (BooleanExpression) ((SoyProtoValue) args.get(0)).getProto(); SoyValue tiersList = NullData.INSTANCE; if (args.size() > 1) { tiersList = args.get(1); } String expression = apply(spec, tiersList, tierPrefixedFunction, findVmTierFunction); return StringData.forValue(expression); } public static String apply(BooleanExpression spec, SoyValue tiersList, SoyFunctions.TierPrefixed tierPrefixedFunction, SoyFunctions.FindVmTier findVmTierFunction) { List<String> result = new ArrayList<>(); if (spec.hasBooleanDeployInputField()) { BooleanDeployInputField field = spec.getBooleanDeployInputField(); String fieldName = DeployInputFieldName.formatFieldName(field.getName()); String propertyExpression = String.format("properties().%s", fieldName); if (field.getNegated()) { propertyExpression = "!" + propertyExpression; } result.add(propertyExpression); } if (spec.hasHasExternalIp()) { ExternalIpAvailability externalIp = spec.getHasExternalIp(); String noneType = Type.NONE.name(); SoyValue tierName = StringData.forValue(externalIp.getTier()); SoyValue tierSpec = NullData.INSTANCE; if (!externalIp.getTier().isEmpty()) { tierSpec = findVmTierFunction.computeForJava(ImmutableList.of(tierName, tiersList)); } String externalIpProperty = tierPrefixedFunction .computeForJava(ImmutableList.of(StringData.forValue("externalIP"), tierSpec)) .stringValue(); if (externalIp.getNegated()) { result.add(String.format("properties().%s == \"%s\"", externalIpProperty, noneType)); } else { result.add(String.format("properties().%s != \"%s\"", externalIpProperty, noneType)); } } if (result.isEmpty()) { throw new IllegalArgumentException("No property or hasExternalIP was set."); } return String.join(String.format(" %s ", AND), result); } } @Singleton @SoyFunctionSignature( name = "booleanExpressionJinjaExpression", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.BooleanExpression"}, returnType = "string") }) static final class BooleanExpressionJinjaExpression extends TypedSoyFunction implements SoyJavaFunction { @Inject BooleanExpressionJinjaExpression() {} @Override public SoyValue computeForJava(List<SoyValue> args) { BooleanExpression spec = (BooleanExpression) ((SoyProtoValue) args.get(0)).getProto(); return StringData.forValue(apply(spec)); } public static String apply(BooleanExpression spec) { if (!spec.hasBooleanDeployInputField()) { return ""; } BooleanDeployInputField field = spec.getBooleanDeployInputField(); String fieldName = DeployInputFieldName.formatFieldName(field.getName()); String propertyExpression = String.format("properties[\"%s\"]", fieldName); if (field.getNegated()) { propertyExpression = "!" + propertyExpression; } return propertyExpression; } } /** * Examines a (MultiVm|SingleVm)DeploymentPackageSpec and tells whether it contains an {@code * AcceleratorSpec} */ @Singleton @SoyFunctionSignature( name = "solutionHasGpus", value = { @Signature( parameterTypes = { "cloud.deploymentmanager.autogen.MultiVmDeploymentPackageSpec|cloud.deploymentmanager.autogen.SingleVmDeploymentPackageSpec" }, returnType = "bool") }) static final class SolutionHasGpus extends TypedSoyFunction implements SoyJavaFunction { @Inject SolutionHasGpus() {} /** * Argument can be either a {@code SingleVmDeploymentPackageSpec} or a {@code * MultiVmDeploymentPackageSpec} instance */ @Override public SoyValue computeForJava(List<SoyValue> args) { Message value = ((SoyProtoValue) args.get(0)).getProto(); if (value instanceof SingleVmDeploymentPackageSpec) { return SoyValueConverter.INSTANCE .convert(!((SingleVmDeploymentPackageSpec) value).getAcceleratorsList().isEmpty()) .resolve(); } List<VmTierSpec> tierList; try { tierList = extractTierList(args.get(0)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unexpected 2nd arg type for dependentTiers", e); } for (VmTierSpec tier : tierList) { if (!tier.getAcceleratorsList().isEmpty()) { return SoyValueConverter.INSTANCE.convert(true).resolve(); } } return SoyValueConverter.INSTANCE.convert(false).resolve(); } } /** * Extracts as a flattened list of fields from {@code DeployInputSpec}. * This function can take an additional argument of type {@code VmTierSpec}, in which case it * will return only fields that are used by the specified tier. */ @Singleton @SoyFunctionSignature( name = "listDeployInputFields", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.DeployInputSpec"}, returnType = "list<cloud.deploymentmanager.autogen.DeployInputField>"), @Signature( parameterTypes = { "cloud.deploymentmanager.autogen.DeployInputSpec", "cloud.deploymentmanager.autogen.VmTierSpec" }, returnType = "list<cloud.deploymentmanager.autogen.DeployInputField>") }) static final class ListDeployInputFields extends TypedSoyFunction implements SoyJavaFunction { @Inject ListDeployInputFields() {} @Override public SoyValue computeForJava(List<SoyValue> args) { DeployInputSpec inputSpec = (DeployInputSpec) ((SoyProtoValue) args.get(0)).getProto(); if (args.size() == 1) { return SoyValueConverter.INSTANCE.convert(apply(inputSpec)).resolve(); } else { VmTierSpec tierSpec = (VmTierSpec) ((SoyProtoValue) args.get(1)).getProto(); return SoyValueConverter.INSTANCE.convert(apply(inputSpec, tierSpec)).resolve(); } } @VisibleForTesting static List<DeployInputField> apply(DeployInputSpec inputSpec) { List<DeployInputField> fields = new ArrayList<>(); for (DeployInputSection section : inputSpec.getSectionsList()) { fields.addAll(section.getFieldsList()); } return fields; } @VisibleForTesting static List<DeployInputField> apply(DeployInputSpec inputSpec, VmTierSpec tierSpec) { Set<String> referencedFields = buildReferencedFieldsSet(tierSpec); List<DeployInputField> fields = new ArrayList<>(); for (DeployInputSection section : inputSpec.getSectionsList()) { for (DeployInputField field : section.getFieldsList()) { if (referencedFields.contains(field.getName())) { fields.add(field); } } } return fields; } private static Set<String> buildReferencedFieldsSet(VmTierSpec tierSpec) { List<GceMetadataItem> metadataItems = tierSpec.getGceMetadataItemsList(); Set<String> fields = new HashSet<>(); for (GceMetadataItem metadataItem : metadataItems) { if (metadataItem.getValueSpecCase() == VALUE_FROM_DEPLOY_INPUT_FIELD) { fields.add(metadataItem.getValueFromDeployInputField()); } } if (tierSpec.hasLocalSsds() && tierSpec.getLocalSsds().getCountSpecCase() == COUNT_FROM_DEPLOY_INPUT_FIELD) { fields.add(tierSpec.getLocalSsds().getCountFromDeployInputField()); } for (DiskSpec disk : tierSpec.getAdditionalDisksList()) { if (disk.getDeviceNameSuffix().getDeviceNameCase() == DeviceNameCase.NAME_FROM_DEPLOY_INPUT_FIELD) { fields.add(disk.getDeviceNameSuffix().getNameFromDeployInputField()); } } return fields; } } private static List<VmTierSpec> extractTierList(SoyValue tiersArg) { if (tiersArg instanceof SoyList) { List<? extends SoyValue> list = ((SoyList) tiersArg).asResolvedJavaList(); return list.stream() .map(soyValue -> (VmTierSpec) ((SoyProtoValue) soyValue).getProto()) .collect(Collectors.toList()); } else if (tiersArg instanceof SoyProtoValue) { return ((MultiVmDeploymentPackageSpec) ((SoyProtoValue) tiersArg).getProto()).getTiersList(); } else { throw new IllegalArgumentException("Unable to extract tier list from argument"); } } abstract static class AbstractDiskPropertyName extends TypedSoyFunction implements SoyJavaFunction { private final SoyFunctions.TierPrefixed tierPrefixedFunction; AbstractDiskPropertyName(SoyFunctions.TierPrefixed tierPrefixedFunction) { this.tierPrefixedFunction = tierPrefixedFunction; } @Override public SoyValue computeForJava(List<SoyValue> args) { // Index is starting with 0 for the first element, we want to start with 1 int diskPosition = args.get(0).integerValue() + 1; SoyValue baseName = StringData.forValue(getPropertyBaseName(diskPosition)); SoyValue tierSpec = NullData.INSTANCE; if (args.size() > 1) { tierSpec = args.get(1); } return tierPrefixedFunction.computeForJava(Arrays.asList(baseName, tierSpec)); } protected abstract String getPropertyBaseName(int diskPosition); } @Singleton @SoyFunctionSignature( name = "diskTypePropertyName", value = { @Signature( parameterTypes = {"int"}, returnType = "string"), @Signature( parameterTypes = {"int", "cloud.deploymentmanager.autogen.VmTierSpec|null|undefined"}, returnType = "string") }) static final class AdditionalDiskTypePropertyName extends AbstractDiskPropertyName { @Inject AdditionalDiskTypePropertyName(TierPrefixed tierPrefixedFunction) { super(tierPrefixedFunction); } @Override protected String getPropertyBaseName(int diskPosition) { return String.format("%s_type", additionalDiskPropertyName(diskPosition)); } } @Singleton @SoyFunctionSignature( name = "diskSizePropertyName", value = { @Signature( parameterTypes = {"int"}, returnType = "string"), @Signature( parameterTypes = {"int", "cloud.deploymentmanager.autogen.VmTierSpec|null|undefined"}, returnType = "string") }) static final class AdditionalDiskSizePropertyName extends AbstractDiskPropertyName { @Inject AdditionalDiskSizePropertyName(TierPrefixed tierPrefixedFunction) { super(tierPrefixedFunction); } @Override protected String getPropertyBaseName(int diskPosition) { return String.format("%s_sizeGb", additionalDiskPropertyName(diskPosition)); } } @Singleton @SoyFunctionSignature( name = "externalIpTypeName", value = { @Signature( parameterTypes = {"cloud.deploymentmanager.autogen.ExternalIpSpec.Type"}, returnType = "string") }) static final class ExternalIpTypeName extends TypedSoyFunction implements SoyJavaFunction { @Inject ExternalIpTypeName() {} @Override public SoyValue computeForJava(List<SoyValue> args) { ExternalIpSpec.Type type = ExternalIpSpec.Type.forNumber(args.get(0).integerValue()); return StringData.forValue(type.name()); } } private static String additionalDiskPropertyName(int diskPosition) { return String.format("disk%d", diskPosition); } private SoyFunctions() {} }