diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2970acd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# Dockerfile for building artifacts +FROM gradle:5.4.1-jdk8 +MAINTAINER chris@thinkdataworks.com + +ENV APPDIR /app +ENV PROTOC_VERSION 3.5.1 + +WORKDIR $APPDIR +RUN mkdir -p $APPDIR + +RUN apt-get update -yqq && \ + apt-get install -yqq build-essential + +ENV CLASSPATH ".:/usr/local/lib:$CLASSPATH" + +# install protoc; used for protobuff stuff +RUN curl -OL https://storage.googleapis.com/tdw-static/mirrored/protoc-${PROTOC_VERSION}-linux-x86_64.zip && \ + unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d protoc3 && \ + mv protoc3/bin/* /usr/local/bin/ && \ + mv protoc3/include/* /usr/local/include/ + +COPY . $APPDIR \ No newline at end of file diff --git a/README.md b/README.md index f7f438f..3b847ba 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,12 @@ To build the various components, run the following: git clone git@github.com:devork/flit.git cd flit ./gradlew clean build pack + +### Building with docker + +```bash + $ docker-compose run gen ./gradlew build pack +``` ### Installation @@ -66,7 +72,7 @@ The flit plugin accepts the following plugin parameters: | Name | Required | Type | Description | |:--------------|:---------:|:------------------------------|:----------------------------------------------------------| -| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc | +| `target` | Y | `enum[server,client]` | The type of target to generate e.g. server, client etc | | `type` | Y | `enum[spring,undertow,boot]` | Type of target to generate | | `context` | N | `string` | Base context for routing, default is `/twirp` | diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..836f6d1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: "3.4" + +services: + gen: + build: . + volumes: + - .:/app + command: sleep 10000 \ No newline at end of file diff --git a/plugin/build.gradle b/plugin/build.gradle index db5e390..1354e33 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -40,6 +40,7 @@ dependencies { compile 'com.google.protobuf:protobuf-java:3.5.1' compile 'com.google.protobuf:protobuf-java-util:3.5.1' compile 'com.squareup:javapoet:1.11.1' + compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.0.0' compileOnly('org.projectlombok:lombok:+') diff --git a/plugin/gradle.properties b/plugin/gradle.properties index beb72cc..166aa4a 100644 --- a/plugin/gradle.properties +++ b/plugin/gradle.properties @@ -1 +1 @@ -version=1.0.0 \ No newline at end of file +version=1.1.0 \ No newline at end of file diff --git a/plugin/src/main/java/com/flit/protoc/Plugin.java b/plugin/src/main/java/com/flit/protoc/Plugin.java index e238204..be1e953 100644 --- a/plugin/src/main/java/com/flit/protoc/Plugin.java +++ b/plugin/src/main/java/com/flit/protoc/Plugin.java @@ -2,6 +2,7 @@ import com.flit.protoc.gen.Generator; import com.flit.protoc.gen.GeneratorException; +import com.flit.protoc.gen.client.okhttp.OkHttpGenerator; import com.flit.protoc.gen.server.spring.SpringGenerator; import com.flit.protoc.gen.server.undertow.UndertowGenerator; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; @@ -53,6 +54,13 @@ private Generator resolveGenerator(Map params) { default: throw new GeneratorException("Unknown server type: " + params.get(PARAM_TYPE).getValue()); } + case "client": + switch(params.get(PARAM_TYPE).getValue()) { + case "okhttp": + return new OkHttpGenerator(); + default: + throw new GeneratorException("Unknown client type: " + params.get(PARAM_TYPE).getValue()); + } default: throw new GeneratorException("Unknown target type: " + params.get(PARAM_TARGET).getValue()); } diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/BaseGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/BaseGenerator.java similarity index 98% rename from plugin/src/main/java/com/flit/protoc/gen/server/BaseGenerator.java rename to plugin/src/main/java/com/flit/protoc/gen/BaseGenerator.java index d7076e5..f953c06 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/BaseGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/BaseGenerator.java @@ -1,4 +1,4 @@ -package com.flit.protoc.gen.server; +package com.flit.protoc.gen; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java b/plugin/src/main/java/com/flit/protoc/gen/TypeMapper.java similarity index 98% rename from plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java rename to plugin/src/main/java/com/flit/protoc/gen/TypeMapper.java index 5a459d9..1e3598c 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java +++ b/plugin/src/main/java/com/flit/protoc/gen/TypeMapper.java @@ -1,4 +1,4 @@ -package com.flit.protoc.gen.server; +package com.flit.protoc.gen; import com.google.protobuf.DescriptorProtos; import com.squareup.javapoet.ClassName; diff --git a/plugin/src/main/java/com/flit/protoc/gen/client/BaseClientGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/client/BaseClientGenerator.java new file mode 100644 index 0000000..d5d7e3c --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/client/BaseClientGenerator.java @@ -0,0 +1,38 @@ +package com.flit.protoc.gen.client; + +import com.flit.protoc.Parameter; +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.Generator; +import com.flit.protoc.gen.TypeMapper; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.compiler.PluginProtos; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.flit.protoc.Parameter.PARAM_CONTEXT; + +public abstract class BaseClientGenerator implements Generator { + @Override public List generate(PluginProtos.CodeGeneratorRequest request, Map params) { + List files = new ArrayList<>(); + String context = getContext(params); + TypeMapper mapper = new TypeMapper(request.getProtoFileList()); + request.getProtoFileList().forEach(proto -> { + proto.getServiceList().forEach(s -> { + files.addAll(new ClientGenerator(proto, s, mapper).getFiles()); + files.addAll(getRpcGenerator(proto, s, context, mapper).getFiles()); + }); + }); + return files; + } + + private static String getContext(Map params) { + if (params.containsKey(PARAM_CONTEXT)) { + return params.get(PARAM_CONTEXT).getValue(); + } + return null; + } + + protected abstract BaseGenerator getRpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper); +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/client/ClientGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/client/ClientGenerator.java new file mode 100644 index 0000000..46db9df --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/client/ClientGenerator.java @@ -0,0 +1,38 @@ +package com.flit.protoc.gen.client; + +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.TypeMapper; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.compiler.PluginProtos; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; + +import javax.lang.model.element.Modifier; +import java.util.Collections; +import java.util.List; + +public class ClientGenerator extends BaseGenerator { + private final TypeSpec.Builder rpcInterface; + + public ClientGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s, TypeMapper mapper) { + super(proto, s, mapper); + rpcInterface = TypeSpec.interfaceBuilder(ClassName.get(javaPackage, "Rpc" + service.getName())); + rpcInterface.addModifiers(Modifier.PUBLIC); + service.getMethodList().forEach(this::addDispatchMethod); + } + + private void addDispatchMethod(DescriptorProtos.MethodDescriptorProto m) { + rpcInterface.addMethod(MethodSpec.methodBuilder("dispatch" + m.getName()) + .addParameter(mapper.get(m.getInputType()), "in") + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .returns(mapper.get(m.getOutputType())) + .addException(Exception.class) + .build()); + } + + @Override + public List getFiles() { + return Collections.singletonList(toFile(getServiceInterface(), rpcInterface.build())); + } +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/client/Types.java b/plugin/src/main/java/com/flit/protoc/gen/client/Types.java new file mode 100644 index 0000000..76c4c50 --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/client/Types.java @@ -0,0 +1,20 @@ +package com.flit.protoc.gen.client; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.ParameterizedTypeName; + +import java.util.function.Function; + +public class Types { + public static final ClassName String = ClassName.get(String.class); + public static final ClassName OkHttpClient = ClassName.bestGuess("okhttp3.OkHttpClient"); + public static final ClassName Exception = ClassName.get(java.lang.Exception.class); + public static final ClassName RequestBody = ClassName.bestGuess("okhttp3.RequestBody"); + public static final ClassName RequestBuilder = ClassName.bestGuess("okhttp3.Request.Builder"); + public static final ClassName MediaType = ClassName.bestGuess("okhttp3.MediaType"); + public static final ClassName InputStream = ClassName.bestGuess("java.io.InputStream"); + public static final ClassName HttpUrl = ClassName.bestGuess("okhttp3.HttpUrl"); + public static final ClassName Request = ClassName.bestGuess("okhttp3.Request"); + public static final ClassName Response = ClassName.bestGuess("okhttp3.Response"); + public static final ParameterizedTypeName BeforeRequestFunction = ParameterizedTypeName.get(ClassName.bestGuess("java.util.function.Function"), ClassName.bestGuess("BeforeRequestPayload"), RequestBuilder); +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/client/okhttp/OkHttpGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/client/okhttp/OkHttpGenerator.java new file mode 100644 index 0000000..a4ba2ec --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/client/okhttp/OkHttpGenerator.java @@ -0,0 +1,14 @@ +package com.flit.protoc.gen.client.okhttp; + +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.client.BaseClientGenerator; +import com.flit.protoc.gen.TypeMapper; +import com.google.protobuf.DescriptorProtos; + +public class OkHttpGenerator extends BaseClientGenerator { + @Override + protected BaseGenerator getRpcGenerator( + DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) { + return new RpcGenerator(proto, service, context, mapper); + } +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/client/okhttp/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/client/okhttp/RpcGenerator.java new file mode 100644 index 0000000..6f8221f --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/client/okhttp/RpcGenerator.java @@ -0,0 +1,129 @@ +package com.flit.protoc.gen.client.okhttp; + +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.TypeMapper; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.compiler.PluginProtos; +import com.squareup.javapoet.*; + +import java.util.Collections; +import java.util.List; + +import static com.flit.protoc.gen.server.Types.ErrorCode; +import static com.flit.protoc.gen.server.Types.FlitException; +import static javax.lang.model.element.Modifier.*; +import static com.flit.protoc.gen.client.Types.*; + +public class RpcGenerator extends BaseGenerator { + private final String context; + private final TypeSpec.Builder rpcDispatcher; + + RpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) { + super(proto, service, mapper); + this.context = getContext(context); + rpcDispatcher = TypeSpec.classBuilder(getClientName(service)) + .addModifiers(PUBLIC); + addRequestPayload(); + addStaticFields(); + addInstanceFields(); + addConstructor(); + addBeforeRequestFunctionSetter(); + service.getMethodList().forEach(this::writeDispatchMethod); + } + + private ClassName getClientName(DescriptorProtos.ServiceDescriptorProto service) { + return ClassName.get(javaPackage, "Rpc" + service.getName() + "Dispatcher"); + } + + private void addBeforeRequestFunctionSetter() { + rpcDispatcher.addMethod( + MethodSpec.methodBuilder("setBeforeRequest") + .addModifiers(PUBLIC) + .addParameter(BeforeRequestFunction, "beforeRequest") + .addStatement("this.beforeRequest = beforeRequest") + .build() + ); + } + + private void addRequestPayload() { + // Add inner class for request payload + TypeSpec.Builder payloadBuilder = TypeSpec.classBuilder("BeforeRequestPayload").addModifiers(PUBLIC); + payloadBuilder.addMethod(MethodSpec.constructorBuilder() + .addModifiers(PUBLIC) + .addParameter(RequestBuilder, "builder") + .addParameter(HttpUrl, "url") + .addStatement("this.builder = builder") + .addStatement("this.url = url") + .build()); + payloadBuilder.addField(FieldSpec.builder(RequestBuilder, "builder").addModifiers(PUBLIC).build()); + payloadBuilder.addField(FieldSpec.builder(HttpUrl, "url").addModifiers(PUBLIC).build()); + + // Add to dispatcher + rpcDispatcher.addType(payloadBuilder.build()); + } + + private void addStaticFields() { + // Add service path prix + rpcDispatcher.addField(FieldSpec.builder(String, "SERVICE_PATH_PREFIX") + .addModifiers(PUBLIC, STATIC, FINAL) + .initializer("\"$L/$L$L/\"", context, proto.hasPackage() ? proto.getPackage() + "." : "", service.getName()) + .build()); + } + + private void addInstanceFields() { + rpcDispatcher.addField(FieldSpec.builder(String, "baseAddress").addModifiers(PRIVATE, FINAL).build()); + rpcDispatcher.addField(FieldSpec.builder(OkHttpClient, "client").addModifiers(PRIVATE).build()); + rpcDispatcher.addField(FieldSpec.builder(BeforeRequestFunction, "beforeRequest").build()); + } + + private void addConstructor() { + rpcDispatcher.addMethod(MethodSpec.constructorBuilder() + .addModifiers(PUBLIC) + .addParameter(String, "address") + .addParameter(OkHttpClient, "client") + .addStatement("this.baseAddress = address") + .addStatement("this.client = client") + .build()); + } + + private void writeDispatchMethod(DescriptorProtos.MethodDescriptorProto m) { + ClassName inputType = mapper.get(m.getInputType()); + ClassName outputType = mapper.get(m.getOutputType()); + rpcDispatcher.addMethod( + MethodSpec.methodBuilder("dispatch" + m.getName()) + .addModifiers(PUBLIC) + .addParameter(inputType, "in") + .returns(outputType) + .addException(Exception) + .addStatement("$T url = $T.parse(baseAddress + SERVICE_PATH_PREFIX + $S)", HttpUrl, HttpUrl, m.getName()) + .addStatement("$T requestBody = $T.create(in.toByteArray(), $T.get(\"application/protobuf\"))", RequestBody, RequestBody, MediaType) + .addStatement("$T builder = new $T()", RequestBuilder, RequestBuilder) + .beginControlFlow("if(beforeRequest != null)") + .addStatement("builder = beforeRequest.apply(new BeforeRequestPayload(builder, url))") + .endControlFlow() + .addStatement("builder.addHeader(\"Accept\", \"application/protobuf\")") + .addStatement("builder.addHeader(\"Content-Type\", \"application/protobuf\")") + .addStatement("builder.addHeader(\"Flit-Version\", \"v1.1.0\")") + .addStatement("builder.url(url)") + .addStatement("builder.post(requestBody)") + .addStatement("$T request = builder.build()", Request) + .addStatement("String responseString") + .addStatement("$T responseStream", InputStream) + .beginControlFlow("try($T response = client.newCall(request).execute())", Response) + .beginControlFlow("if(response.code() != 200)") + .addStatement("responseString = response.body().string()") + .addStatement("throw $T.builder().withErrorCode($T.INTERNAL).withMeta(\"message\", responseString).withMessage(\"RPC error\").build()", FlitException, ErrorCode) + .nextControlFlow("else") + .addStatement("responseStream = response.body().byteStream()") + .addStatement("return $T.parseFrom(responseStream)", outputType) + .endControlFlow() + .endControlFlow() + .build() + ); + } + + @Override + public List getFiles() { + return Collections.singletonList(toFile(getClientName(service), rpcDispatcher.build())); + } +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/BaseServerGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/BaseServerGenerator.java index cb94509..208f8f2 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/BaseServerGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/BaseServerGenerator.java @@ -1,7 +1,9 @@ package com.flit.protoc.gen.server; import com.flit.protoc.Parameter; +import com.flit.protoc.gen.BaseGenerator; import com.flit.protoc.gen.Generator; +import com.flit.protoc.gen.TypeMapper; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/ServiceGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/ServiceGenerator.java index 4eb993d..d308580 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/ServiceGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/ServiceGenerator.java @@ -1,5 +1,7 @@ package com.flit.protoc.gen.server; +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.TypeMapper; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; import com.squareup.javapoet.ClassName; @@ -34,7 +36,8 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .build()); } - @Override public List getFiles() { + @Override + public List getFiles() { return Collections.singletonList(toFile(getServiceInterface(), rpcInterface.build())); } } diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/spring/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/spring/RpcGenerator.java index 2ea7459..bf275bc 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/spring/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/spring/RpcGenerator.java @@ -1,7 +1,7 @@ package com.flit.protoc.gen.server.spring; -import com.flit.protoc.gen.server.BaseGenerator; -import com.flit.protoc.gen.server.TypeMapper; +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.TypeMapper; import com.flit.protoc.gen.server.Types; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/spring/SpringGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/spring/SpringGenerator.java index abfe4f3..c5ddd3d 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/spring/SpringGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/spring/SpringGenerator.java @@ -1,8 +1,8 @@ package com.flit.protoc.gen.server.spring; -import com.flit.protoc.gen.server.BaseGenerator; +import com.flit.protoc.gen.BaseGenerator; import com.flit.protoc.gen.server.BaseServerGenerator; -import com.flit.protoc.gen.server.TypeMapper; +import com.flit.protoc.gen.TypeMapper; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/undertow/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/undertow/RpcGenerator.java index c751944..6383015 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/undertow/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/undertow/RpcGenerator.java @@ -1,7 +1,7 @@ package com.flit.protoc.gen.server.undertow; -import com.flit.protoc.gen.server.BaseGenerator; -import com.flit.protoc.gen.server.TypeMapper; +import com.flit.protoc.gen.BaseGenerator; +import com.flit.protoc.gen.TypeMapper; import com.flit.protoc.gen.server.Types; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/undertow/UndertowGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/undertow/UndertowGenerator.java index fa11771..5124551 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/undertow/UndertowGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/undertow/UndertowGenerator.java @@ -1,8 +1,8 @@ package com.flit.protoc.gen.server.undertow; -import com.flit.protoc.gen.server.BaseGenerator; +import com.flit.protoc.gen.BaseGenerator; import com.flit.protoc.gen.server.BaseServerGenerator; -import com.flit.protoc.gen.server.TypeMapper; +import com.flit.protoc.gen.TypeMapper; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; diff --git a/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/HelloworldGeneratorTest.java new file mode 100644 index 0000000..091bfac --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/HelloworldGeneratorTest.java @@ -0,0 +1,30 @@ +package com.flit.protoc.gen.client.okhttp; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import org.approvaltests.Approvals; +import org.junit.Test; + +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class HelloworldGeneratorTest extends BaseGeneratorTest { + + @Test public void test_Generate() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.okhttp.json"); + + Plugin plugin = new Plugin(request); + PluginProtos.CodeGeneratorResponse response = plugin.process(); + + assertNotNull(response); + assertEquals(2, response.getFileCount()); + assertEquals(response.getFile(0).getName(), "com/example/helloworld/RpcHelloWorld.java"); + assertEquals(response.getFile(1).getName(), "com/example/helloworld/RpcHelloWorldDispatcher.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(f -> f.getContent()).collect(toList())); + response.getFileList().forEach(f -> assertParses(f)); + } + +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/HelloworldGeneratorTest.test_Generate.approved.txt new file mode 100644 index 0000000..60f839e --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/HelloworldGeneratorTest.test_Generate.approved.txt @@ -0,0 +1,107 @@ +[0] = package com.example.helloworld; + +import java.lang.Exception; + +public interface RpcHelloWorld { + Helloworld.HelloResp dispatchHello(Helloworld.HelloReq in) throws Exception; + + Helloworld.HelloResp dispatchHelloAgain(Helloworld.HelloReq in) throws Exception; +} + +[1] = package com.example.helloworld; + +import com.flit.runtime.ErrorCode; +import com.flit.runtime.FlitException; +import java.io.InputStream; +import java.lang.Exception; +import java.lang.String; +import java.util.function.Function; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +public class RpcHelloWorldDispatcher { + public static final String SERVICE_PATH_PREFIX = "/twirp/com.example.helloworld.HelloWorld/"; + + private final String baseAddress; + + private OkHttpClient client; + + Function beforeRequest; + + public RpcHelloWorldDispatcher(String address, OkHttpClient client) { + this.baseAddress = address; + this.client = client; + } + + public void setBeforeRequest(Function beforeRequest) { + this.beforeRequest = beforeRequest; + } + + public Helloworld.HelloResp dispatchHello(Helloworld.HelloReq in) throws Exception { + HttpUrl url = HttpUrl.parse(baseAddress + SERVICE_PATH_PREFIX + "Hello"); + RequestBody requestBody = RequestBody.create(in.toByteArray(), MediaType.get("application/protobuf")); + Request.Builder builder = new Request.Builder(); + if(beforeRequest != null) { + builder = beforeRequest.apply(new BeforeRequestPayload(builder, url)); + } + builder.addHeader("Accept", "application/protobuf"); + builder.addHeader("Content-Type", "application/protobuf"); + builder.addHeader("Flit-Version", "v1.1.0"); + builder.url(url); + builder.post(requestBody); + Request request = builder.build(); + String responseString; + InputStream responseStream; + try(Response response = client.newCall(request).execute()) { + if(response.code() != 200) { + responseString = response.body().string(); + throw FlitException.builder().withErrorCode(ErrorCode.INTERNAL).withMeta("message", responseString).withMessage("RPC error").build(); + } else { + responseStream = response.body().byteStream(); + return Helloworld.HelloResp.parseFrom(responseStream); + } + } + } + + public Helloworld.HelloResp dispatchHelloAgain(Helloworld.HelloReq in) throws Exception { + HttpUrl url = HttpUrl.parse(baseAddress + SERVICE_PATH_PREFIX + "HelloAgain"); + RequestBody requestBody = RequestBody.create(in.toByteArray(), MediaType.get("application/protobuf")); + Request.Builder builder = new Request.Builder(); + if(beforeRequest != null) { + builder = beforeRequest.apply(new BeforeRequestPayload(builder, url)); + } + builder.addHeader("Accept", "application/protobuf"); + builder.addHeader("Content-Type", "application/protobuf"); + builder.addHeader("Flit-Version", "v1.1.0"); + builder.url(url); + builder.post(requestBody); + Request request = builder.build(); + String responseString; + InputStream responseStream; + try(Response response = client.newCall(request).execute()) { + if(response.code() != 200) { + responseString = response.body().string(); + throw FlitException.builder().withErrorCode(ErrorCode.INTERNAL).withMeta("message", responseString).withMessage("RPC error").build(); + } else { + responseStream = response.body().byteStream(); + return Helloworld.HelloResp.parseFrom(responseStream); + } + } + } + + public class BeforeRequestPayload { + public Request.Builder builder; + + public HttpUrl url; + + public BeforeRequestPayload(Request.Builder builder, HttpUrl url) { + this.builder = builder; + this.url = url; + } + } +} + diff --git a/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/StatusGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/StatusGeneratorTest.java new file mode 100644 index 0000000..7f35253 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/StatusGeneratorTest.java @@ -0,0 +1,34 @@ +package com.flit.protoc.gen.client.okhttp; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import org.approvaltests.Approvals; +import org.junit.Test; + +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Tests the generation of a service that has core definition imported from another file + */ +public class StatusGeneratorTest extends BaseGeneratorTest { + + @Test public void test_Generate() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("status.okhttp.json"); + + Plugin plugin = new Plugin(request); + PluginProtos.CodeGeneratorResponse response = plugin.process(); + + assertNotNull(response); + assertEquals(2, response.getFileCount()); + + assertEquals(response.getFile(0).getName(), "com/example/helloworld/RpcStatus.java"); + assertEquals(response.getFile(1).getName(), "com/example/helloworld/RpcStatusDispatcher.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(f -> f.getContent()).collect(toList())); + response.getFileList().forEach(f -> assertParses(f)); + } + +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/StatusGeneratorTest.test_Generate.approved.txt new file mode 100644 index 0000000..ef7e9f7 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/client/okhttp/StatusGeneratorTest.test_Generate.approved.txt @@ -0,0 +1,79 @@ +[0] = package com.example.helloworld; + +import java.lang.Exception; + +public interface RpcStatus { + StatusOuterClass.StatusResponse dispatchGetStatus(Core.Empty in) throws Exception; +} + +[1] = package com.example.helloworld; + +import com.flit.runtime.ErrorCode; +import com.flit.runtime.FlitException; +import java.io.InputStream; +import java.lang.Exception; +import java.lang.String; +import java.util.function.Function; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +public class RpcStatusDispatcher { + public static final String SERVICE_PATH_PREFIX = "/twirp/com.example.helloworld.Status/"; + + private final String baseAddress; + + private OkHttpClient client; + + Function beforeRequest; + + public RpcStatusDispatcher(String address, OkHttpClient client) { + this.baseAddress = address; + this.client = client; + } + + public void setBeforeRequest(Function beforeRequest) { + this.beforeRequest = beforeRequest; + } + + public StatusOuterClass.StatusResponse dispatchGetStatus(Core.Empty in) throws Exception { + HttpUrl url = HttpUrl.parse(baseAddress + SERVICE_PATH_PREFIX + "GetStatus"); + RequestBody requestBody = RequestBody.create(in.toByteArray(), MediaType.get("application/protobuf")); + Request.Builder builder = new Request.Builder(); + if(beforeRequest != null) { + builder = beforeRequest.apply(new BeforeRequestPayload(builder, url)); + } + builder.addHeader("Accept", "application/protobuf"); + builder.addHeader("Content-Type", "application/protobuf"); + builder.addHeader("Flit-Version", "v1.1.0"); + builder.url(url); + builder.post(requestBody); + Request request = builder.build(); + String responseString; + InputStream responseStream; + try(Response response = client.newCall(request).execute()) { + if(response.code() != 200) { + responseString = response.body().string(); + throw FlitException.builder().withErrorCode(ErrorCode.INTERNAL).withMeta("message", responseString).withMessage("RPC error").build(); + } else { + responseStream = response.body().byteStream(); + return StatusOuterClass.StatusResponse.parseFrom(responseStream); + } + } + } + + public class BeforeRequestPayload { + public Request.Builder builder; + + public HttpUrl url; + + public BeforeRequestPayload(Request.Builder builder, HttpUrl url) { + this.builder = builder; + this.url = url; + } + } +} + diff --git a/plugin/src/test/resources/helloworld.okhttp.json b/plugin/src/test/resources/helloworld.okhttp.json new file mode 100644 index 0000000..55ff634 --- /dev/null +++ b/plugin/src/test/resources/helloworld.okhttp.json @@ -0,0 +1,49 @@ +{ + "fileToGenerate": ["helloworld.proto"], + "parameter": "target=client,type=okhttp", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "helloworld.proto", + "package": "com.example.helloworld", + "messageType": [{ + "name": "HelloReq", + "field": [{ + "name": "subject", + "number": 1, + "label": "LABEL_OPTIONAL", + "type": "TYPE_STRING", + "jsonName": "subject" + }] + }, { + "name": "HelloResp", + "field": [{ + "name": "text", + "number": 1, + "label": "LABEL_OPTIONAL", + "type": "TYPE_STRING", + "jsonName": "text" + }] + }], + "service": [{ + "name": "HelloWorld", + "method": [{ + "name": "Hello", + "inputType": ".com.example.helloworld.HelloReq", + "outputType": ".com.example.helloworld.HelloResp" + }, { + "name": "HelloAgain", + "inputType": ".com.example.helloworld.HelloReq", + "outputType": ".com.example.helloworld.HelloResp" + }] + }], + "options": { + "javaPackage": "com.example.helloworld" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/status.okhttp.json b/plugin/src/test/resources/status.okhttp.json new file mode 100644 index 0000000..cde91ab --- /dev/null +++ b/plugin/src/test/resources/status.okhttp.json @@ -0,0 +1,79 @@ +{ + "fileToGenerate": ["core.proto", "status.proto"], + "parameter": "target=client,type=okhttp", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "core.proto", + "package": "com.example.helloworld", + "messageType": [{ + "name": "Empty" + }], + "options": { + "javaPackage": "com.example.helloworld" + }, + "syntax": "proto3" + }, { + "name": "status.proto", + "package": "com.example.helloworld", + "dependency": ["core.proto"], + "messageType": [{ + "name": "StatusResponse", + "field": [{ + "name": "status", + "number": 1, + "label": "LABEL_OPTIONAL", + "type": "TYPE_ENUM", + "typeName": ".com.example.helloworld.StatusResponse.StatusType", + "jsonName": "status" + }, { + "name": "sha", + "number": 2, + "label": "LABEL_OPTIONAL", + "type": "TYPE_STRING", + "jsonName": "sha" + }, { + "name": "date", + "number": 3, + "label": "LABEL_OPTIONAL", + "type": "TYPE_STRING", + "jsonName": "date" + }, { + "name": "version", + "number": 4, + "label": "LABEL_OPTIONAL", + "type": "TYPE_STRING", + "jsonName": "version" + }], + "enumType": [{ + "name": "StatusType", + "value": [{ + "name": "UNKNOWN", + "number": 0 + }, { + "name": "RUNNING", + "number": 1 + }, { + "name": "ERROR", + "number": 2 + }] + }] + }], + "service": [{ + "name": "Status", + "method": [{ + "name": "GetStatus", + "inputType": ".com.example.helloworld.Empty", + "outputType": ".com.example.helloworld.StatusResponse" + }] + }], + "options": { + "javaPackage": "com.example.helloworld" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/status.proto b/plugin/src/test/resources/status.proto index 4fd97a9..9db689e 100644 --- a/plugin/src/test/resources/status.proto +++ b/plugin/src/test/resources/status.proto @@ -3,7 +3,6 @@ syntax = "proto3"; package com.example.helloworld; option java_package = "com.example.helloworld"; - import "core.proto"; // ----------------------------------------------------------------------------------- diff --git a/runtime/core/build.gradle b/runtime/core/build.gradle index ee87f0f..a0f3438 100644 --- a/runtime/core/build.gradle +++ b/runtime/core/build.gradle @@ -15,5 +15,7 @@ dependencies { compile "com.google.protobuf:protobuf-java:${protobufVersion}" compile "com.google.protobuf:protobuf-java-util:${protobufVersion}" + compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.0.0' + testCompile 'junit:junit:4.12' } diff --git a/runtime/spring/gradle.properties b/runtime/spring/gradle.properties index beb72cc..166aa4a 100644 --- a/runtime/spring/gradle.properties +++ b/runtime/spring/gradle.properties @@ -1 +1 @@ -version=1.0.0 \ No newline at end of file +version=1.1.0 \ No newline at end of file diff --git a/runtime/undertow/gradle.properties b/runtime/undertow/gradle.properties index beb72cc..166aa4a 100644 --- a/runtime/undertow/gradle.properties +++ b/runtime/undertow/gradle.properties @@ -1 +1 @@ -version=1.0.0 \ No newline at end of file +version=1.1.0 \ No newline at end of file