All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qmetry.qaf.automation.grpc.GrpcClientUtil Maven / Gradle / Ivy

/**
 * 
 */
package com.qmetry.qaf.automation.grpc;

import static com.qmetry.qaf.automation.core.ConfigurationManager.getBundle;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.json.JSONException;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.MethodDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.qmetry.qaf.automation.core.AutomationError;
import com.qmetry.qaf.automation.core.ConfigurationManager;
import com.qmetry.qaf.automation.core.LoggingBean;
import com.qmetry.qaf.automation.core.TestBaseProvider;
import com.qmetry.qaf.automation.util.FileUtil;
import com.qmetry.qaf.automation.util.JSONUtil;
import com.qmetry.qaf.automation.util.StringUtil;

import io.grpc.StatusException;
import io.grpc.StatusRuntimeException;
import me.dinowernli.grpc.polyglot.command.ServiceCall;
import me.dinowernli.grpc.polyglot.command.ServiceList;
import me.dinowernli.grpc.polyglot.config.ConfigurationLoader;
import me.dinowernli.grpc.polyglot.io.Output;
import me.dinowernli.grpc.polyglot.protobuf.ProtocInvoker;
import me.dinowernli.grpc.polyglot.protobuf.ProtocInvoker.ProtocInvocationException;
import me.dinowernli.grpc.polyglot.protobuf.ServiceResolver;
import polyglot.ConfigProto.Configuration;
import polyglot.ConfigProto.Configuration.Builder;
import polyglot.ConfigProto.ProtoConfiguration;

/**
 * @author chirag.jayswal
 *
 */
public class GrpcClientUtil {
	public static final String GRPC_RESPONSE_KEY = "_grpc.response";

	public static GrpcResponse call(GrpcRequestBean request) {
		TestBaseProvider.instance().get().getContext().clearProperty(GRPC_RESPONSE_KEY);
		GrpcResponse response = null;
		String message = "";
		try {

			if (StringUtil.isBlank(request.getBody())) {
				request.setBody("{}");
			}

			if (StringUtil.isBlank(request.getProto_discovery_root())) {
				String protoFilePath = getProtoFilePath(request.getEndPoint());
				if(StringUtil.isNotBlank(protoFilePath)) {
					request.setProto_discovery_root(protoFilePath);
				}else {
					request.setProto_discovery_root(getBundle().getString("grpc.proto_discovery_root"));
				}
			}
			final byte[] payload = request.getBody().getBytes();
			final ByteArrayInputStream payloadStream = new ByteArrayInputStream(payload);
			System.setIn(payloadStream);

			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
			final String utf8 = StandardCharsets.UTF_8.name();
			
			String proto_discovery_root = request.getProto_discovery_root();
			String config_set_path = request.getConfig_set_path();
			String config_name = getBundle().getString("grpc.config_name");

			final ConfigurationLoader configLoader = maybeInputPath(config_set_path)
					.map(ConfigurationLoader::forFile).orElseGet(() -> ConfigurationLoader.forDefaultConfigSet());// .withOverrides(arguments);

			Builder configBuilder = Optional.ofNullable(config_name)
					.map(configLoader::getNamedConfiguration).orElseGet(() -> configLoader.getDefaultConfiguration())
					.toBuilder();
			configBuilder.getProtoConfigBuilder()
					.setProtoDiscoveryRoot(proto_discovery_root);
			Configuration config = configBuilder.build();

			ProtoConfiguration protoConfig = config.getProtoConfig();
			try (PrintStream ps = new PrintStream(baos, true, utf8)) {
				Output output = Output.forStream(ps);
				long stTime = System.currentTimeMillis();
				Long duration = 0l;

				try {
					ServiceCall.callEndpoint(output, protoConfig, Optional.of(request.getBaseUrl()),
							Optional.of(request.getEndPoint()),
							maybeInputPath(proto_discovery_root),
							maybeInputPath(config_set_path), ImmutableList.of(),
							config.getCallConfig());
					duration = (System.currentTimeMillis() - stTime);

					message = baos.toString(utf8);
					response = new GrpcResponse(message);
				} catch (Exception e) {
					duration = (System.currentTimeMillis() - stTime);
					Throwable cause = causeFromThrowable(e);
					if (baos.size() <= 0) {
						cause.printStackTrace(ps);
					}
					message = baos.toString(utf8);
					response = new GrpcResponse(message, causeFromThrowable(e));
				}

				TestBaseProvider.instance().get().getContext().setProperty(GRPC_RESPONSE_KEY, response);

				// add command log
				String status = response.getStatus().getCode().name();
				if (StringUtil.isNotBlank(response.getStatus().getDescription())) {
					status = status + " : " + response.getStatus().getDescription();
				}
				LoggingBean requestLog = new LoggingBean("grpc://" + request.getBaseUrl(),
						new String[] { request.getEndPoint() }, status);

				LoggingBean respLog = new LoggingBean("", new String[] { request.getBody() }, message);
				respLog.setDuration(duration.intValue());
				requestLog.getSubLogs().add(respLog);

				TestBaseProvider.instance().get().getLog().add(requestLog);

			} 
		} catch (Exception | Error e) {
			throw new AutomationError(e);
		}
		return response;
	}

	public static GrpcResponse getGrpcResponse() {
		return (GrpcResponse) TestBaseProvider.instance().get().getContext().getProperty(GRPC_RESPONSE_KEY);
	}
	
	public static List listMethods(String protoDiscoveryRoot) throws ProtocInvocationException {

		final ConfigurationLoader configLoader = maybeInputPath(
				getBundle().getString("grpc.config_set_path", "resources/proto/grpc.config.json"))
				.map(ConfigurationLoader::forFile).orElseGet(() -> ConfigurationLoader.forDefaultConfigSet());// .withOverrides(arguments
																												// )

		Builder configBuilder = Optional.ofNullable(getBundle().getString("grpc.config_name"))
				.map(configLoader::getNamedConfiguration).orElseGet(() -> configLoader.getDefaultConfiguration())
				.toBuilder();
		configBuilder.getProtoConfigBuilder().setProtoDiscoveryRoot(protoDiscoveryRoot);

		Configuration config = configBuilder.build();

		FileDescriptorSet fileDescriptorSet = getFileDescriptorSet(config.getProtoConfig());

		ServiceResolver serviceResolver = ServiceResolver.fromFileDescriptorSet(fileDescriptorSet);
		List methods = new ArrayList<>();
		for (ServiceDescriptor descriptor : serviceResolver.listServices()) {

			try {
				methods.addAll(descriptor.getMethods());
			} catch (Throwable e) {
				e.printStackTrace();
				System.err.println("ERROR:: " + descriptor.getFullName());
			}
		}
		return methods;
	}
	public static String renderDescriptor(Descriptor descriptor, String indent) {
		if (descriptor.getFields().size() == 0) {
			return indent + "";
		}

		List fieldsAsStrings = descriptor.getFields().stream()
				.map(field -> renderDescriptor(field, indent + "  ")).collect(Collectors.toList());

		return Joiner.on(System.lineSeparator()).join(fieldsAsStrings);
	}

	private static String renderDescriptor(FieldDescriptor descriptor, String indent) {
		String isOpt = descriptor.isOptional() ? "" : "";
		String isRep = descriptor.isRepeated() ? "" : "";
		String fieldPrefix = indent + descriptor.getJsonName() + "[" + isOpt + " " + isRep + "]";

		if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
			return fieldPrefix + " {" + System.lineSeparator()
					+ renderDescriptor(descriptor.getMessageType(), indent + "  ") + System.lineSeparator() + indent
					+ "}";

		} else if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) {
			return fieldPrefix + ": " + descriptor.getEnumType().getValues();

		} else {
			return fieldPrefix + ": " + descriptor.getJavaType();
		}
	}
	public static void list() {
		try {
			final ConfigurationLoader configLoader = maybeInputPath(getBundle().getString("grpc.config_set_path"))
					.map(ConfigurationLoader::forFile).orElseGet(() -> ConfigurationLoader.forDefaultConfigSet());// .withOverrides(arguments);

			Builder configBuilder = Optional.ofNullable(getBundle().getString("grpc.config_name"))
					.map(configLoader::getNamedConfiguration).orElseGet(() -> configLoader.getDefaultConfiguration())
					.toBuilder();
			configBuilder.getProtoConfigBuilder()
					.setProtoDiscoveryRoot(getBundle().getString("grpc.proto_discovery_root"));
			Configuration config = configBuilder.build();
			ProtoConfiguration protoConfig = config.getProtoConfig();
				Output output = Output.forStream(new PrintStream(System.out) {public void print(String s) {super.print("Start::" +s);};});

				try {
			        FileDescriptorSet fileDescriptorSet = getFileDescriptorSet(config.getProtoConfig());
					ServiceList.listServices(output, fileDescriptorSet, protoConfig.getProtoDiscoveryRoot(), Optional.ofNullable(null), Optional.ofNullable(null), Optional.ofNullable(true));
				} catch (Exception e) {
					e.printStackTrace();
				}

		} catch (Exception | Error e) {
			throw new AutomationError(e);
		}
	}
	
	public static void list(String file) {
		
		try {
			final ConfigurationLoader configLoader = maybeInputPath(getBundle().getString("grpc.config_set_path"))
					.map(ConfigurationLoader::forFile).orElseGet(() -> ConfigurationLoader.forDefaultConfigSet());// .withOverrides(arguments);

			Builder configBuilder = Optional.ofNullable(getBundle().getString("grpc.config_name"))
					.map(configLoader::getNamedConfiguration).orElseGet(() -> configLoader.getDefaultConfiguration())
					.toBuilder();
			configBuilder.getProtoConfigBuilder()
					.setProtoDiscoveryRoot(getBundle().getString("grpc.proto_discovery_root",new File(file).getParentFile().getPath()));
			Configuration config = configBuilder.build();

			ProtoConfiguration protoConfig = config.getProtoConfig();
				Output output = Output.forStream(new PrintStream(System.out) {public void print(String s) {super.print("Start::" +s);};});

				try {
			        FileDescriptorSet fileDescriptorSet = getFileDescriptorSet(config.getProtoConfig());
					ServiceList.listServices(output, fileDescriptorSet, protoConfig.getProtoDiscoveryRoot(), Optional.ofNullable(null), Optional.ofNullable(null), Optional.ofNullable(true));
				} catch (Exception e) {
					e.printStackTrace();
				}
		} catch (Exception | Error e) {
			throw new AutomationError(e);
		}
	}

	 /** Invokes protoc and returns a {@link FileDescriptorSet} used for discovery. 
	 * @throws ProtocInvocationException */
	  private static FileDescriptorSet getFileDescriptorSet(ProtoConfiguration protoConfig) throws ProtocInvocationException {
	    ProtocInvoker invoker = ProtocInvoker.forConfig(protoConfig);
	    return invoker.invoke();
	  }

	private static Optional maybeOutputPath(String rawPath) {
		if (rawPath == null) {
			return Optional.empty();
		}
		return Optional.of(Paths.get(rawPath));
	}

	private static Optional maybeInputPath(String rawPath) {
		return maybeOutputPath(rawPath).map(path -> {
			Preconditions.checkArgument(Files.exists(path), "File " + rawPath + " does not exist");
			return path;
		});
	}

	private static Throwable causeFromThrowable(Throwable t) {
		Throwable cause = t.getCause() == null ? t : t.getCause();
		while (cause != null) {
			if (cause instanceof StatusException || cause instanceof StatusRuntimeException) {
				return cause;
			}
			cause = cause.getCause();
		}
		// Couldn't find a cause with a Status
		return t;
	}
	
	private static String getProtoFilePath(String endpoint) {
		String methodDescriptorKey="grpcmethod."+endpoint.replace("/", ".").trim();

		String methodDescriptor=getBundle().getString(methodDescriptorKey);
		if(StringUtil.isBlank(methodDescriptor)) {
    		String root = ConfigurationManager.getBundle().getString("grpc.proto_discovery_root", "resources/proto");
    		File discoveryFile = new File(root,"proto-discovery.properties");

    		if(discoveryFile.exists()) {
    			ConfigurationManager.getBundle().load(new String[] {discoveryFile.getPath()});
				methodDescriptor=getBundle().getString(methodDescriptorKey);
    		}
    		if(StringUtil.isBlank(methodDescriptor)) {
    			Collection allProtoFiles = FileUtil.getFiles(new File(root), "proto", true); 
    			PropertiesConfiguration prop = new PropertiesConfiguration();
    			prop.setEncoding("UTF-8");

    			//Properties prop = new Properties();
    			try(FileInputStream fis = new FileInputStream(discoveryFile)) {
					prop.load(fis);
				} catch (IOException | ConfigurationException e1) {
				}
    			for(File protoFile : allProtoFiles) {
					try {
						List methods = listMethods(protoFile.getPath());
						for(MethodDescriptor m:methods) {
							int lastDot = m.getFullName().lastIndexOf('.');
							StringBuilder methodName = new StringBuilder(m.getFullName());
							methodName.replace(lastDot, lastDot+1, "/");
							prop.setProperty("grpcmethod." +m.getFullName(), String.format("{'endpoint':'%s','proto_discovery_root':'%s'}",methodName, protoFile.getPath()));
						}
					} catch (ProtocInvocationException e) {
					}
    			}
    			try(FileOutputStream out = new FileOutputStream(discoveryFile)) {
					prop.save(out, "UTF-8");
				} catch (IOException | ConfigurationException e) {
					e.printStackTrace();
				}
    			ConfigurationManager.getBundle().load(new String[] {discoveryFile.getPath()});
				methodDescriptor=getBundle().getString(methodDescriptorKey);
    		}
		}
		if(StringUtil.isNotBlank(methodDescriptor)) {
			try {
				return (String) JSONUtil.toMap(methodDescriptor).getOrDefault("proto_discovery_root","");
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}
		return "";
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy