use of com.google.api.generator.gapic.model.Field in project gapic-generator-java by googleapis.
the class HttpRuleParser method validateAndConstructHttpBindings.
private static Set<HttpBinding> validateAndConstructHttpBindings(Set<String> paramNames, Message inputMessage, Map<String, Message> messageTypes, Map<String, String> patternSampleValues) {
ImmutableSortedSet.Builder<HttpBinding> httpBindings = ImmutableSortedSet.naturalOrder();
for (String paramName : paramNames) {
// Handle foo.bar cases by descending into the subfields.
String patternSampleValue = patternSampleValues != null ? patternSampleValues.get(paramName) : null;
String[] subFields = paramName.split("\\.");
if (inputMessage == null) {
httpBindings.add(HttpBinding.create(paramName, false, patternSampleValue));
continue;
}
Message nestedMessage = inputMessage;
for (int i = 0; i < subFields.length; i++) {
String subFieldName = subFields[i];
if (i < subFields.length - 1) {
Field field = nestedMessage.fieldMap().get(subFieldName);
nestedMessage = messageTypes.get(field.type().reference().fullName());
Preconditions.checkNotNull(nestedMessage, String.format("No containing message found for field %s with type %s", field.name(), field.type().reference().simpleName()));
} else {
if (patternSampleValues != null) {
checkHttpFieldIsValid(subFieldName, nestedMessage, false);
patternSampleValue = patternSampleValues.get(paramName);
}
Field field = nestedMessage.fieldMap().get(subFieldName);
httpBindings.add(HttpBinding.create(paramName, field.isProto3Optional(), patternSampleValue));
}
}
}
return httpBindings.build();
}
use of com.google.api.generator.gapic.model.Field in project gapic-generator-java by googleapis.
the class HttpRuleParser method checkHttpFieldIsValid.
private static void checkHttpFieldIsValid(String binding, Message inputMessage, boolean isBody) {
Preconditions.checkState(!Strings.isNullOrEmpty(binding), String.format("Null or empty binding for " + inputMessage.name()));
Preconditions.checkState(inputMessage.fieldMap().containsKey(binding), String.format("Expected message %s to contain field %s but none found", inputMessage.name(), binding));
Field field = inputMessage.fieldMap().get(binding);
boolean fieldCondition = !field.isRepeated();
if (!isBody) {
fieldCondition &= field.type().isProtoPrimitiveType() || field.isEnum();
}
String messageFormat = "Expected a non-repeated " + (isBody ? "" : "primitive ") + "type for field %s in message %s but got type %s";
Preconditions.checkState(fieldCondition, String.format(messageFormat, field.name(), inputMessage.name(), field.type()));
}
use of com.google.api.generator.gapic.model.Field in project gapic-generator-java by googleapis.
the class MethodSignatureParser method parseMethodSignatures.
/**
* Parses a list of method signature annotations out of an RPC.
*/
public static List<List<MethodArgument>> parseMethodSignatures(MethodDescriptor methodDescriptor, String servicePackage, TypeNode methodInputType, Map<String, Message> messageTypes, Map<String, ResourceName> resourceNames, Set<ResourceName> outputArgResourceNames) {
List<String> stringSigs = methodDescriptor.getOptions().getExtension(ClientProto.methodSignature);
List<List<MethodArgument>> signatures = new ArrayList<>();
if (stringSigs.isEmpty()) {
return signatures;
}
Map<String, ResourceName> patternsToResourceNames = ResourceParserHelpers.createPatternResourceNameMap(resourceNames);
Message inputMessage = messageTypes.get(methodInputType.reference().fullName());
// stringSigs: ["content,error", "content,error,info"].
for (String stringSig : stringSigs) {
if (Strings.isNullOrEmpty(stringSig)) {
signatures.add(Collections.emptyList());
continue;
}
List<String> argumentNames = new ArrayList<>();
Map<String, List<MethodArgument>> argumentNameToOverloads = new HashMap<>();
// stringSig.split: ["content", "error"].
for (String argumentName : stringSig.split(METHOD_SIGNATURE_DELIMITER)) {
// For resource names, this will be empty.
List<Field> argumentFieldPathAcc = new ArrayList<>();
// There should be more than one type returned only when we encounter a resource name.
Map<TypeNode, Field> argumentTypes = parseTypeFromArgumentName(argumentName, servicePackage, inputMessage, messageTypes, resourceNames, patternsToResourceNames, argumentFieldPathAcc, outputArgResourceNames);
int dotLastIndex = argumentName.lastIndexOf(DOT);
String actualArgumentName = dotLastIndex < 0 ? argumentName : argumentName.substring(dotLastIndex + 1);
argumentNames.add(actualArgumentName);
argumentNameToOverloads.put(actualArgumentName, argumentTypes.entrySet().stream().map(e -> MethodArgument.builder().setName(actualArgumentName).setType(e.getKey()).setField(e.getValue()).setIsResourceNameHelper(argumentTypes.size() > 1 && !e.getKey().equals(TypeNode.STRING)).setNestedFields(argumentFieldPathAcc).build()).collect(Collectors.toList()));
}
signatures.addAll(flattenMethodSignatureVariants(argumentNames, argumentNameToOverloads));
}
// Make the method signature order deterministic, which helps with unit testing and per-version
// diffs.
List<List<MethodArgument>> sortedMethodSignatures = signatures.stream().sorted((s1, s2) -> {
// Sort by number of arguments first.
if (s1.size() != s2.size()) {
return s1.size() - s2.size();
}
// Then by MethodSignature properties.
for (int i = 0; i < s1.size(); i++) {
int compareVal = s1.get(i).compareTo(s2.get(i));
if (compareVal != 0) {
return compareVal;
}
}
return 0;
}).collect(Collectors.toList());
return sortedMethodSignatures;
}
use of com.google.api.generator.gapic.model.Field in project gapic-generator-java by googleapis.
the class Parser method parseField.
private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor messageDescriptor, boolean hasFieldNameConflict, Set<ResourceReference> outputResourceReferencesSeen) {
FieldOptions fieldOptions = fieldDescriptor.getOptions();
MessageOptions messageOptions = messageDescriptor.getOptions();
ResourceReference resourceReference = null;
if (fieldOptions.hasExtension(ResourceProto.resourceReference)) {
com.google.api.ResourceReference protoResourceReference = fieldOptions.getExtension(ResourceProto.resourceReference);
// Assumes only one of type or child_type is set.
String typeString = protoResourceReference.getType();
String childTypeString = protoResourceReference.getChildType();
Preconditions.checkState(!Strings.isNullOrEmpty(typeString) ^ !Strings.isNullOrEmpty(childTypeString), String.format("Exactly one of type or child_type must be set for resource_reference in field %s", fieldDescriptor.getName()));
boolean isChildType = !Strings.isNullOrEmpty(childTypeString);
resourceReference = isChildType ? ResourceReference.withChildType(childTypeString) : ResourceReference.withType(typeString);
outputResourceReferencesSeen.add(resourceReference);
} else if (messageOptions.hasExtension(ResourceProto.resource)) {
ResourceDescriptor protoResource = messageOptions.getExtension(ResourceProto.resource);
// aip.dev/4231.
String resourceFieldNameValue = ResourceNameConstants.NAME_FIELD_NAME;
if (!Strings.isNullOrEmpty(protoResource.getNameField())) {
resourceFieldNameValue = protoResource.getNameField();
}
if (fieldDescriptor.getName().equals(resourceFieldNameValue)) {
resourceReference = ResourceReference.withType(protoResource.getType());
}
}
Field.Builder fieldBuilder = Field.builder();
if (fieldDescriptor.getFile().toProto().hasSourceCodeInfo()) {
SourceCodeInfoLocation protoFieldLocation = SOURCE_CODE_INFO_PARSER.getLocation(fieldDescriptor);
if (!Objects.isNull(protoFieldLocation) && !Strings.isNullOrEmpty(protoFieldLocation.getLeadingComments())) {
fieldBuilder.setDescription(protoFieldLocation.getLeadingComments());
}
}
// Mirror protoc's name conflict resolution behavior for fields.
// For more context, trace hasFieldNameConflict back to where it gets passed in above.
String actualFieldName = hasFieldNameConflict ? fieldDescriptor.getName() + fieldDescriptor.getNumber() : fieldDescriptor.getName();
return fieldBuilder.setName(actualFieldName).setOriginalName(fieldDescriptor.getName()).setType(TypeParser.parseType(fieldDescriptor)).setIsMessage(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE).setIsEnum(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.ENUM).setIsContainedInOneof(fieldDescriptor.getContainingOneof() != null && !fieldDescriptor.getContainingOneof().isSynthetic()).setIsProto3Optional(fieldDescriptor.getContainingOneof() != null && fieldDescriptor.getContainingOneof().isSynthetic()).setIsRepeated(fieldDescriptor.isRepeated()).setIsMap(fieldDescriptor.isMapField()).setResourceReference(resourceReference).build();
}
use of com.google.api.generator.gapic.model.Field in project gapic-generator-java by googleapis.
the class Parser method parse.
public static GapicContext parse(CodeGeneratorRequest request) {
Optional<String> gapicYamlConfigPathOpt = PluginArgumentParser.parseGapicYamlConfigPath(request);
Optional<List<GapicBatchingSettings>> batchingSettingsOpt = BatchingSettingsConfigParser.parse(gapicYamlConfigPathOpt);
Optional<List<GapicLroRetrySettings>> lroRetrySettingsOpt = GapicLroRetrySettingsParser.parse(gapicYamlConfigPathOpt);
Optional<GapicLanguageSettings> languageSettingsOpt = GapicLanguageSettingsParser.parse(gapicYamlConfigPathOpt);
Optional<String> transportOpt = PluginArgumentParser.parseTransport(request);
boolean willGenerateMetadata = PluginArgumentParser.hasMetadataFlag(request);
Optional<String> serviceConfigPathOpt = PluginArgumentParser.parseJsonConfigPath(request);
Optional<GapicServiceConfig> serviceConfigOpt = ServiceConfigParser.parse(serviceConfigPathOpt.orElse(null));
if (serviceConfigOpt.isPresent()) {
GapicServiceConfig serviceConfig = serviceConfigOpt.get();
serviceConfig.setLroRetrySettings(lroRetrySettingsOpt);
serviceConfig.setBatchingSettings(batchingSettingsOpt);
serviceConfig.setLanguageSettings(languageSettingsOpt);
serviceConfigOpt = Optional.of(serviceConfig);
}
Optional<String> serviceYamlConfigPathOpt = PluginArgumentParser.parseServiceYamlConfigPath(request);
Optional<com.google.api.Service> serviceYamlProtoOpt = serviceYamlConfigPathOpt.flatMap(ServiceYamlParser::parse);
// Collect the resource references seen in messages.
Set<ResourceReference> outputResourceReferencesSeen = new HashSet<>();
// Keep message and resource name parsing separate for cleaner logic.
// While this takes an extra pass through the protobufs, the extra time is relatively trivial
// and is worth the larger reduced maintenance cost.
Map<String, Message> messages = parseMessages(request, outputResourceReferencesSeen);
Map<String, ResourceName> resourceNames = parseResourceNames(request);
messages = updateResourceNamesInMessages(messages, resourceNames.values());
// Contains only resource names that are actually used. Usage refers to the presence of a
// request message's field in an RPC's method_signature annotation. That is, resource name
// definitions
// or references that are simply defined, but not used in such a manner, will not have
// corresponding Java helper
// classes generated.
Set<ResourceName> outputArgResourceNames = new HashSet<>();
List<Service> mixinServices = new ArrayList<>();
Transport transport = Transport.parse(transportOpt.orElse(Transport.GRPC.toString()));
List<Service> services = parseServices(request, messages, resourceNames, outputArgResourceNames, serviceYamlProtoOpt, serviceConfigOpt, mixinServices, transport);
Preconditions.checkState(!services.isEmpty(), "No services found to generate");
// Temporary workaround for Ads, who still need these resource names.
if (services.get(0).protoPakkage().startsWith("google.ads.googleads.v")) {
Function<ResourceName, String> typeNameFn = r -> r.resourceTypeString().substring(r.resourceTypeString().indexOf("/") + 1);
Function<Set<ResourceName>, Set<String>> typeStringSetFn = sr -> sr.stream().map(typeNameFn).collect(Collectors.toSet());
// Include all resource names present in message types for backwards-compatibility with the
// monolith. In the future, this should be removed on a client library major semver update.
// Resolve type name collisions with the ones present in the method arguments.
final Set<String> typeStringSet = typeStringSetFn.apply(outputArgResourceNames);
outputArgResourceNames.addAll(resourceNames.values().stream().filter(r -> r.hasParentMessageName() && !typeStringSet.contains(typeNameFn.apply(r))).collect(Collectors.toSet()));
String servicePackage = services.get(0).pakkage();
Map<String, ResourceName> patternsToResourceNames = ResourceParserHelpers.createPatternResourceNameMap(resourceNames);
for (ResourceReference resourceReference : outputResourceReferencesSeen) {
final Set<String> interimTypeStringSet = typeStringSetFn.apply(outputArgResourceNames);
outputArgResourceNames.addAll(ResourceReferenceParser.parseResourceNames(resourceReference, servicePackage, null, resourceNames, patternsToResourceNames).stream().filter(r -> !interimTypeStringSet.contains(typeNameFn.apply(r))).collect(Collectors.toSet()));
}
}
return GapicContext.builder().setServices(services).setMixinServices(mixinServices).setMessages(messages).setResourceNames(resourceNames).setHelperResourceNames(outputArgResourceNames).setServiceConfig(serviceConfigOpt.orElse(null)).setGapicMetadataEnabled(willGenerateMetadata).setServiceYamlProto(serviceYamlProtoOpt.orElse(null)).setTransport(transport).build();
}
Aggregations