package me.dinowernli.grpc.polyglot.command; import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.auth.Credentials; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.net.HostAndPort; import com.google.protobuf.DescriptorProtos.FileDescriptorSet; import com.google.protobuf.Descriptors.MethodDescriptor; import com.google.protobuf.DynamicMessage; import io.grpc.CallOptions; import io.grpc.stub.StreamObserver; import me.dinowernli.grpc.polyglot.grpc.CompositeStreamObserver; import me.dinowernli.grpc.polyglot.grpc.DynamicGrpcClient; import me.dinowernli.grpc.polyglot.io.LoggingStatsWriter; import me.dinowernli.grpc.polyglot.io.MessageReader; import me.dinowernli.grpc.polyglot.io.MessageWriter; import me.dinowernli.grpc.polyglot.io.Output; import me.dinowernli.grpc.polyglot.oauth2.OauthCredentialsFactory; import me.dinowernli.grpc.polyglot.protobuf.ProtoMethodName; import me.dinowernli.grpc.polyglot.protobuf.ServiceResolver; import polyglot.ConfigProto.CallConfiguration; /** Makes a call to an endpoint, rendering the result */ public class ServiceCall { private static final Logger logger = LoggerFactory.getLogger(ServiceCall.class); /** Calls the endpoint specified in the arguments */ public static void callEndpoint( Output output, FileDescriptorSet fileDescriptorSet, Optional<String> endpoint, Optional<String> fullMethod, Optional<Path> protoDiscoveryRoot, Optional<Path> configSetPath, ImmutableList<Path> additionalProtocIncludes, CallConfiguration callConfig) { Preconditions.checkState(endpoint.isPresent(), "--endpoint argument required"); Preconditions.checkState(fullMethod.isPresent(), "--full_method argument required"); validatePath(protoDiscoveryRoot); validatePath(configSetPath); validatePaths(additionalProtocIncludes); HostAndPort hostAndPort = HostAndPort.fromString(endpoint.get()); ProtoMethodName grpcMethodName = ProtoMethodName.parseFullGrpcMethodName(fullMethod.get()); ServiceResolver serviceResolver = ServiceResolver.fromFileDescriptorSet(fileDescriptorSet); MethodDescriptor methodDescriptor = serviceResolver.resolveServiceMethod(grpcMethodName); logger.info("Creating dynamic grpc client"); DynamicGrpcClient dynamicClient; if (callConfig.hasOauthConfig()) { Credentials credentials = new OauthCredentialsFactory(callConfig.getOauthConfig()).getCredentials(); dynamicClient = DynamicGrpcClient.createWithCredentials( methodDescriptor, hostAndPort, callConfig, credentials); } else { dynamicClient = DynamicGrpcClient.create(methodDescriptor, hostAndPort, callConfig); } ImmutableList<DynamicMessage> requestMessages = MessageReader.forStdin(methodDescriptor.getInputType()).read(); StreamObserver<DynamicMessage> streamObserver = CompositeStreamObserver.of(new LoggingStatsWriter(), MessageWriter.create(output)); logger.info(String.format( "Making rpc with %d request(s) to endpoint [%s]", requestMessages.size(), hostAndPort)); try { dynamicClient.call(requestMessages, streamObserver, callOptions(callConfig)).get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException("Caught exeception while waiting for rpc", e); } } private static CallOptions callOptions(CallConfiguration callConfig) { CallOptions result = CallOptions.DEFAULT; if (callConfig.getDeadlineMs() > 0) { result = result.withDeadlineAfter(callConfig.getDeadlineMs(), TimeUnit.MILLISECONDS); } return result; } private static void validatePath(Optional<Path> maybePath) { if (maybePath.isPresent()) { Preconditions.checkArgument(Files.exists(maybePath.get())); } } private static void validatePaths(Iterable<Path> paths) { for (Path path : paths) { Preconditions.checkArgument(Files.exists(path)); } } }