use of com.google.api.DocumentationRule in project gapic-generator-java by googleapis.
the class Parser method parseServices.
public static List<Service> parseServices(CodeGeneratorRequest request, Map<String, Message> messageTypes, Map<String, ResourceName> resourceNames, Set<ResourceName> outputArgResourceNames, Optional<com.google.api.Service> serviceYamlProtoOpt, Optional<GapicServiceConfig> serviceConfigOpt, List<Service> outputMixinServices, Transport transport) {
Map<String, FileDescriptor> fileDescriptors = getFilesToGenerate(request);
List<Service> services = new ArrayList<>();
for (String fileToGenerate : request.getFileToGenerateList()) {
FileDescriptor fileDescriptor = Preconditions.checkNotNull(fileDescriptors.get(fileToGenerate), "Missing file descriptor for [%s]", fileToGenerate);
services.addAll(parseService(fileDescriptor, messageTypes, resourceNames, serviceYamlProtoOpt, serviceConfigOpt, outputArgResourceNames, transport));
}
// Prevent codegen for mixed-in services if there are other services present, since that is an
// indicator that we are not generating a GAPIC client for the mixed-in service on its own.
Function<Service, String> serviceFullNameFn = s -> String.format("%s.%s", s.protoPakkage(), s.name());
Set<Service> blockedCodegenMixinApis = new HashSet<>();
Set<Service> definedServices = new HashSet<>();
for (Service s : services) {
if (MIXIN_ALLOWLIST.contains(serviceFullNameFn.apply(s))) {
blockedCodegenMixinApis.add(s);
} else {
definedServices.add(s);
}
}
// It's very unlikely the blocklisted APIs will contain the other, or any other service.
boolean servicesContainBlocklistedApi = !blockedCodegenMixinApis.isEmpty() && !definedServices.isEmpty();
// Service names that are stated in the YAML file (as mixins). Used to filter
// blockedCodegenMixinApis.
Set<String> mixedInApis = !serviceYamlProtoOpt.isPresent() ? Collections.emptySet() : serviceYamlProtoOpt.get().getApisList().stream().filter(a -> MIXIN_ALLOWLIST.contains(a.getName())).map(a -> a.getName()).collect(Collectors.toSet());
// Holds the methods to be mixed in.
// Key: proto_package.ServiceName.RpcName.
// Value: HTTP rules, which clobber those in the proto.
// Assumes that http.rules.selector always specifies RPC names in the above format.
Map<String, HttpBindings> mixedInMethodsToHttpRules = new HashMap<>();
Map<String, String> mixedInMethodsToDocs = new HashMap<>();
// Parse HTTP rules and documentation, which will override the proto.
if (serviceYamlProtoOpt.isPresent()) {
for (HttpRule httpRule : serviceYamlProtoOpt.get().getHttp().getRulesList()) {
HttpBindings httpBindings = HttpRuleParser.parseHttpRule(httpRule);
if (httpBindings == null) {
continue;
}
for (String rpcFullNameRaw : httpRule.getSelector().split(",")) {
String rpcFullName = rpcFullNameRaw.trim();
mixedInMethodsToHttpRules.put(rpcFullName, httpBindings);
}
}
for (DocumentationRule docRule : serviceYamlProtoOpt.get().getDocumentation().getRulesList()) {
for (String rpcFullNameRaw : docRule.getSelector().split(",")) {
String rpcFullName = rpcFullNameRaw.trim();
mixedInMethodsToDocs.put(rpcFullName, docRule.getDescription());
}
}
}
// Sort potential mixin services alphabetically.
List<Service> orderedBlockedCodegenMixinApis = blockedCodegenMixinApis.stream().sorted((s1, s2) -> s2.name().compareTo(s1.name())).collect(Collectors.toList());
Set<String> apiDefinedRpcs = new HashSet<>();
for (Service service : services) {
if (orderedBlockedCodegenMixinApis.contains(service)) {
continue;
}
apiDefinedRpcs.addAll(service.methods().stream().map(m -> m.name()).collect(Collectors.toSet()));
}
// Mix-in APIs only if the protos are present and they're defined in the service.yaml file.
Set<Service> outputMixinServiceSet = new HashSet<>();
if (servicesContainBlocklistedApi && !mixedInApis.isEmpty()) {
for (int i = 0; i < services.size(); i++) {
Service originalService = services.get(i);
List<Method> updatedOriginalServiceMethods = new ArrayList<>(originalService.methods());
// If mixin APIs are present, add the methods to all other services.
for (Service mixinService : orderedBlockedCodegenMixinApis) {
final String mixinServiceFullName = serviceFullNameFn.apply(mixinService);
if (!mixedInApis.contains(mixinServiceFullName)) {
continue;
}
Function<Method, String> methodToFullProtoNameFn = m -> String.format("%s.%s", mixinServiceFullName, m.name());
// Filter mixed-in methods based on those listed in the HTTP rules section of
// service.yaml.
List<Method> updatedMixinMethods = mixinService.methods().stream().filter(m -> mixedInMethodsToHttpRules.containsKey(methodToFullProtoNameFn.apply(m))).map(m -> {
// HTTP rules and RPC documentation in the service.yaml file take
// precedence.
String fullMethodName = methodToFullProtoNameFn.apply(m);
HttpBindings httpBindings = mixedInMethodsToHttpRules.containsKey(fullMethodName) ? mixedInMethodsToHttpRules.get(fullMethodName) : m.httpBindings();
String docs = mixedInMethodsToDocs.containsKey(fullMethodName) ? mixedInMethodsToDocs.get(fullMethodName) : m.description();
return m.toBuilder().setHttpBindings(httpBindings).setDescription(docs).build();
}).collect(Collectors.toList());
// Overridden RPCs defined in the protos take precedence.
updatedMixinMethods.stream().filter(m -> !apiDefinedRpcs.contains(m.name())).forEach(m -> updatedOriginalServiceMethods.add(m.toBuilder().setMixedInApiName(serviceFullNameFn.apply(mixinService)).build()));
// Sort by method name, to ensure a deterministic method ordering (for tests).
updatedMixinMethods = updatedMixinMethods.stream().sorted((m1, m2) -> m2.name().compareTo(m1.name())).collect(Collectors.toList());
outputMixinServiceSet.add(mixinService.toBuilder().setMethods(updatedMixinMethods).build());
}
services.set(i, originalService.toBuilder().setMethods(updatedOriginalServiceMethods).build());
}
}
if (servicesContainBlocklistedApi) {
services = services.stream().filter(s -> !MIXIN_ALLOWLIST.contains(serviceFullNameFn.apply(s))).collect(Collectors.toList());
}
// Use a list to ensure ordering for deterministic tests.
outputMixinServices.addAll(outputMixinServiceSet.stream().sorted((s1, s2) -> s2.name().compareTo(s1.name())).collect(Collectors.toList()));
return services;
}
Aggregations