From bb08e7b4b8a127c205df5123ffff7e1f8d5d5eb4 Mon Sep 17 00:00:00 2001 From: Ben Eddy Date: Wed, 2 Jan 2019 19:51:07 -0700 Subject: [PATCH 01/44] Support nested proto files Previously, if `Type` was defined in /nested/file.proto, flit would generate a class reference of nested/File.Type. --- .../main/java/com/flit/protoc/gen/server/TypeMapper.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java b/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java index 5a459d9..837332d 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java @@ -3,6 +3,7 @@ import com.google.protobuf.DescriptorProtos; import com.squareup.javapoet.ClassName; +import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,7 +35,8 @@ public static String getClassname(DescriptorProtos.FileDescriptorProto proto) { if (clazz == null || clazz.isEmpty()) { - char[] classname = proto.getName().substring(0, proto.getName().lastIndexOf('.')).toCharArray(); + String basename = new File(proto.getName()).getName(); + char[] classname = basename.substring(0, basename.lastIndexOf('.')).toCharArray(); StringBuilder sb = new StringBuilder(); char previous = '_'; @@ -70,7 +72,7 @@ public static String getClassname(DescriptorProtos.FileDescriptorProto proto) { } } - return clazz; + return String.join(".", proto.getOptions().getJavaPackage(), clazz); } } From 02bef48c09fc3b40a65696cdd969ef37552ced9f Mon Sep 17 00:00:00 2001 From: Nathan Witmer Date: Thu, 10 Jan 2019 12:16:20 -0700 Subject: [PATCH 02/44] Rename spring runtime build artifact to flit-spring-runtime --- runtime/spring/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/spring/build.gradle b/runtime/spring/build.gradle index 28f463c..e86159b 100644 --- a/runtime/spring/build.gradle +++ b/runtime/spring/build.gradle @@ -3,7 +3,7 @@ plugins { } group 'com.flit' -archivesBaseName = 'flit-undertow-runtime' +archivesBaseName = 'flit-spring-runtime' sourceCompatibility = 1.8 repositories { From c74be90addf0f69ffe90704901c18d787435e236 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Mon, 14 Jan 2019 13:24:41 -0800 Subject: [PATCH 03/44] Correctly handle InvalidProtocolBufferException in controllers --- plugin/src/main/shell/protoc-gen-flit | 2 +- .../runtime/spring/FlitExceptionHandler.java | 25 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/plugin/src/main/shell/protoc-gen-flit b/plugin/src/main/shell/protoc-gen-flit index ec5d7ac..b21b828 100755 --- a/plugin/src/main/shell/protoc-gen-flit +++ b/plugin/src/main/shell/protoc-gen-flit @@ -15,7 +15,7 @@ # The quiet flag is required otherwise the tooling will dump output to stdout which will break the # plugin output and generation will fail. # ----------------------------------------------------------------------------- -DIR=$(dirname $(readlink -f "$0")) +DIR=$(dirname "$0") JAR=$(ls -c ${DIR}/plugin-*-all.jar | head -1) java ${FLIT_JAVA_OPTS} -jar $JAR $@ \ No newline at end of file diff --git a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java index a57c3c9..f1644e5 100644 --- a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java +++ b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java @@ -2,6 +2,10 @@ import com.flit.runtime.ErrorCode; import com.flit.runtime.FlitException; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -11,15 +15,11 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; - @ControllerAdvice @Component public class FlitExceptionHandler extends ResponseEntityExceptionHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionHandler.class); @ExceptionHandler(Exception.class) @@ -40,6 +40,21 @@ public ResponseEntity handleException(HttpServletRequest request, Exception e .body(response); } + @ExceptionHandler(InvalidProtocolBufferException.class) + public ResponseEntity handlehandleInvalidProtocolBufferException(HttpServletRequest request, Exception e) { + LOGGER.error("InvalidProtocolBufferException: request = {}, method = {}, msg= {}", + request.getRequestURI(), request.getMethod(), e.getMessage(), e); + + Map response = new HashMap<>(); + response.put("code", ErrorCode.INVALID_ARGUMENT); + response.put("msg", e.getMessage()); + + return ResponseEntity + .status(ErrorCode.INVALID_ARGUMENT.getHttpStatus()) + .contentType(MediaType.APPLICATION_JSON) + .body(response); + } + @ExceptionHandler(FlitException.class) public ResponseEntity handleFlitException(HttpServletRequest request, FlitException e) { From b8a18dbf70679aa2f3f2e03cff83ade4cf61b762 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Mon, 14 Jan 2019 15:55:36 -0800 Subject: [PATCH 04/44] Replace Invalid JSON msg with exception msg --- plugin/src/main/shell/protoc-gen-flit | 2 +- .../com/flit/runtime/spring/FlitExceptionHandler.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugin/src/main/shell/protoc-gen-flit b/plugin/src/main/shell/protoc-gen-flit index b21b828..ec5d7ac 100755 --- a/plugin/src/main/shell/protoc-gen-flit +++ b/plugin/src/main/shell/protoc-gen-flit @@ -15,7 +15,7 @@ # The quiet flag is required otherwise the tooling will dump output to stdout which will break the # plugin output and generation will fail. # ----------------------------------------------------------------------------- -DIR=$(dirname "$0") +DIR=$(dirname $(readlink -f "$0")) JAR=$(ls -c ${DIR}/plugin-*-all.jar | head -1) java ${FLIT_JAVA_OPTS} -jar $JAR $@ \ No newline at end of file diff --git a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java index f1644e5..bf9e607 100644 --- a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java +++ b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java @@ -3,9 +3,6 @@ import com.flit.runtime.ErrorCode; import com.flit.runtime.FlitException; import com.google.protobuf.InvalidProtocolBufferException; -import java.util.HashMap; -import java.util.Map; -import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -15,11 +12,15 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + @ControllerAdvice @Component public class FlitExceptionHandler extends ResponseEntityExceptionHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionHandler.class); @ExceptionHandler(Exception.class) From 5856d09be44bec9dc5644cf1ed294f044052e458 Mon Sep 17 00:00:00 2001 From: Greg Orzell Date: Mon, 18 Feb 2019 16:23:05 +0100 Subject: [PATCH 05/44] Add tests to the TypeMapper and better super the various proto options that exist for Java. --- .../flit/protoc/gen/server/TypeMapper.java | 95 +++++--- .../protoc/gen/server/TypeMapperTest.java | 207 ++++++++++++++++++ 2 files changed, 268 insertions(+), 34 deletions(-) create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java b/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java index 837332d..cb12e41 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/TypeMapper.java @@ -2,7 +2,6 @@ import com.google.protobuf.DescriptorProtos; import com.squareup.javapoet.ClassName; - import java.io.File; import java.util.HashMap; import java.util.List; @@ -10,7 +9,7 @@ public class TypeMapper { - // holds the qualified class name to the short class reference + // Maps the protobuf package and name to the fully qualified name of the generated Java class. private final Map mapping = new HashMap<>(); public TypeMapper() { @@ -22,7 +21,8 @@ public TypeMapper(List files) { public void add(DescriptorProtos.FileDescriptorProto proto) { proto.getMessageTypeList().forEach(m -> { - mapping.put("." + proto.getPackage() + "." + m.getName(), getClassname(proto) + "." + m.getName()); + mapping.put("." + proto.getPackage() + "." + m.getName(), + getOuterClassOrPackageName(proto) + "." + m.getName()); }); } @@ -30,49 +30,76 @@ public ClassName get(String protobufFqcn) { return ClassName.bestGuess(mapping.get(protobufFqcn)); } - public static String getClassname(DescriptorProtos.FileDescriptorProto proto) { - String clazz = proto.getOptions().getJavaOuterClassname(); + /** + * Determine where message or service in a given proto file will be generated. Depending on the + * java specific options in the spec, this could be either inside of an outer class, or at the top + * level of the package. + */ + public static String getOuterClassOrPackageName(DescriptorProtos.FileDescriptorProto proto) { + // If no 'java_package' option is provided, the protoc compiler will default to the protobuf + // package name. + String packageName = proto.getOptions().hasJavaPackage() ? + proto.getOptions().getJavaPackage() : proto.getPackage(); + + // If this option is enabled protoc will generate a class for each message/service at the top + // level of the given package space. Because message name is appended in the add method, this + // should just return the package in that case. If there are collisions protoc should give a + // warning/error. + if (proto.getOptions().getJavaMultipleFiles()) { + return packageName; + } - if (clazz == null || clazz.isEmpty()) { + // If an outer class name is provided it should be used, otherwise we need to infer one based + // on the same rules the protoc compiler uses. + String outerClass = proto.getOptions().hasJavaOuterClassname() ? + proto.getOptions().getJavaOuterClassname() : outerClassNameFromProtoName(proto); - String basename = new File(proto.getName()).getName(); - char[] classname = basename.substring(0, basename.lastIndexOf('.')).toCharArray(); - StringBuilder sb = new StringBuilder(); + if (outerClass.isEmpty()) { + throw new IllegalArgumentException("'option java_outer_classname' cannot be set to \"\"."); + } - char previous = '_'; - for (char c : classname) { - if (c == '_') { - previous = c; - continue; - } + String fqName = String.join(".", packageName, outerClass); - if (previous == '_') { - sb.append(Character.toUpperCase(c)); - } else { - sb.append(c); - } + // check to see if there are any messages with this same class name as per java proto specs + // note that we also check the services too as the protoc compiler does that as well. + for (DescriptorProtos.DescriptorProto type : proto.getMessageTypeList()) { + if (type.getName().equals(outerClass)) { + return fqName + "OuterClass"; + } + } - previous = c; + for (DescriptorProtos.ServiceDescriptorProto service : proto.getServiceList()) { + if (service.getName().equals(outerClass)) { + return fqName + "OuterClass"; } + } - clazz = sb.toString(); + return fqName; + } + + private static String outerClassNameFromProtoName(DescriptorProtos.FileDescriptorProto proto) { + String basename = new File(proto.getName()).getName(); + char[] classname = basename.substring(0, basename.lastIndexOf('.')).toCharArray(); + StringBuilder sb = new StringBuilder(); - // check to see if there are any messages with this same class name as per java proto specs - // note that we also check the services too as the protoc compiler does that as well - for (DescriptorProtos.DescriptorProto type : proto.getMessageTypeList()) { - if (type.getName().equals(clazz)) { - return clazz + "OuterClass"; - } + char previous = '_'; + for (char c : classname) { + if (c == '_') { + previous = c; + continue; } - for (DescriptorProtos.ServiceDescriptorProto service : proto.getServiceList()) { - if (service.getName().equals(clazz)) { - return clazz + "OuterClass"; - } + if (previous == '_') { + sb.append(Character.toUpperCase(c)); + } else { + sb.append(c); } + + previous = c; } - return String.join(".", proto.getOptions().getJavaPackage(), clazz); - } + String clazz = sb.toString(); + return clazz; + } } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java new file mode 100644 index 0000000..43bfbac --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java @@ -0,0 +1,207 @@ +package com.flit.protoc.gen.server; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import com.google.protobuf.DescriptorProtos; +import com.squareup.javapoet.ClassName; +import org.junit.Test; + +public class TypeMapperTest { + + private static final String PROTO_PACKAGE = "flit.test"; + private static final String JAVA_PACKAGE = "com.flit.test"; + + private static final DescriptorProtos.DescriptorProto MAP_MESSAGE = DescriptorProtos.DescriptorProto + .newBuilder() + .setName("Map") + .build(); + + private static final DescriptorProtos.DescriptorProto MAPPER_MESSAGE = DescriptorProtos.DescriptorProto + .newBuilder() + .setName("Mapper") + .build(); + + @Test + public void protoPackage() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .setJavaOuterClassname("Mapper") + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAP_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Map"); + assertEquals(PROTO_PACKAGE, result.packageName()); + assertEquals("Mapper", result.enclosingClassName().simpleName()); + assertEquals("Map", result.simpleName()); + } + + @Test + public void protoPackageNameCollision() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAPPER_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Mapper"); + assertEquals(PROTO_PACKAGE, result.packageName()); + assertEquals("MapperOuterClass", result.enclosingClassName().simpleName()); + assertEquals("Mapper", result.simpleName()); + } + + @Test + public void protoPackageWithOuterClass() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .setJavaOuterClassname("Mapper") + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAP_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Map"); + assertEquals(PROTO_PACKAGE, result.packageName()); + assertEquals("Mapper", result.enclosingClassName().simpleName()); + assertEquals("Map", result.simpleName()); + } + + @Test + public void javaPackage() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .setJavaPackage(JAVA_PACKAGE) + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAP_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Map"); + assertEquals(JAVA_PACKAGE, result.packageName()); + assertEquals("Mapper", result.enclosingClassName().simpleName()); + assertEquals("Map", result.simpleName()); + } + + @Test + public void javaPackageNameCollision() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .setJavaPackage(JAVA_PACKAGE) + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAPPER_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Mapper"); + assertEquals(JAVA_PACKAGE, result.packageName()); + assertEquals("MapperOuterClass", result.enclosingClassName().simpleName()); + assertEquals("Mapper", result.simpleName()); + } + + @Test + public void javaPackageWithOuterClass() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .setJavaOuterClassname("Mapper") + .setJavaPackage(JAVA_PACKAGE) + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAP_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Map"); + assertEquals(JAVA_PACKAGE, result.packageName()); + assertEquals("Mapper", result.enclosingClassName().simpleName()); + assertEquals("Map", result.simpleName()); + } + + @Test(expected = IllegalArgumentException.class) + public void javaPackageWithOuterClassEmpty() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(false) + .setJavaOuterClassname("") + .setJavaPackage(JAVA_PACKAGE) + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAP_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + mapper.get(".flit.test.Map"); + } + + @Test + public void javaPackageWithOuterClassMultiFile() { + DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() + .setJavaMultipleFiles(true) + .setJavaOuterClassname("Mapper") + .setJavaPackage(JAVA_PACKAGE) + .build(); + + DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() + .setPackage(PROTO_PACKAGE) + .setName("mapper.proto") + .setOptions(options) + .addMessageType(MAP_MESSAGE) + .build(); + + TypeMapper mapper = new TypeMapper(); + mapper.add(proto); + + ClassName result = mapper.get(".flit.test.Map"); + assertEquals(JAVA_PACKAGE, result.packageName()); + assertNull(result.enclosingClassName()); + assertEquals("Map", result.simpleName()); + } +} From bbc0d50f4bdb6bb44e3caaa44af413aacb8694dc Mon Sep 17 00:00:00 2001 From: Greg Orzell Date: Tue, 19 Feb 2019 09:26:25 +0100 Subject: [PATCH 06/44] Add/bump version numbers where appropriate. --- plugin/gradle.properties | 2 +- runtime/core/gradle.properties | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 runtime/core/gradle.properties diff --git a/plugin/gradle.properties b/plugin/gradle.properties index beb72cc..de652db 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 diff --git a/runtime/core/gradle.properties b/runtime/core/gradle.properties new file mode 100644 index 0000000..beb72cc --- /dev/null +++ b/runtime/core/gradle.properties @@ -0,0 +1 @@ +version=1.0.0 \ No newline at end of file From 9e355426547c585256c3923bed4216d70f5c8ad6 Mon Sep 17 00:00:00 2001 From: Greg Orzell Date: Mon, 4 Mar 2019 09:39:52 +0100 Subject: [PATCH 07/44] Fix bug in test that lead to duplicate behavior in two tests. --- .../src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java index 43bfbac..aedd91a 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java @@ -26,7 +26,6 @@ public class TypeMapperTest { public void protoPackage() { DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() .setJavaMultipleFiles(false) - .setJavaOuterClassname("Mapper") .build(); DescriptorProtos.FileDescriptorProto proto = DescriptorProtos.FileDescriptorProto.newBuilder() From aabeebe5cd7a60a51f233d154c7230a1ab7c8c92 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Wed, 19 Aug 2020 11:44:19 -0700 Subject: [PATCH 08/44] Implement JAX-RS service generation --- .../src/main/java/com/flit/protoc/Plugin.java | 5 +- .../gen/server/jaxrs/JaxrsGenerator.java | 16 ++++ .../protoc/gen/server/jaxrs/RpcGenerator.java | 85 +++++++++++++++++++ .../test/java/com/flit/protoc/PluginTest.java | 2 +- .../server/jaxrs/ContextGeneratorTest.java | 67 +++++++++++++++ .../server/jaxrs/HelloworldGeneratorTest.java | 32 +++++++ ...ldGeneratorTest.test_Generate.approved.txt | 52 ++++++++++++ .../gen/server/jaxrs/StatusGeneratorTest.java | 35 ++++++++ ...usGeneratorTest.test_Generate.approved.txt | 36 ++++++++ .../test/resources/context.empty.jaxrs.json | 29 +++++++ .../test/resources/context.missing.jaxrs.json | 29 +++++++ .../test/resources/context.name.jaxrs.json | 29 +++++++ .../test/resources/context.root.jaxrs.json | 29 +++++++ .../test/resources/context.slash.jaxrs.json | 29 +++++++ .../src/test/resources/helloworld.jaxrs.json | 49 +++++++++++ plugin/src/test/resources/status.jaxrs.json | 79 +++++++++++++++++ 16 files changed, 601 insertions(+), 2 deletions(-) create mode 100644 plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java create mode 100644 plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt create mode 100644 plugin/src/test/resources/context.empty.jaxrs.json create mode 100644 plugin/src/test/resources/context.missing.jaxrs.json create mode 100644 plugin/src/test/resources/context.name.jaxrs.json create mode 100644 plugin/src/test/resources/context.root.jaxrs.json create mode 100644 plugin/src/test/resources/context.slash.jaxrs.json create mode 100644 plugin/src/test/resources/helloworld.jaxrs.json create mode 100644 plugin/src/test/resources/status.jaxrs.json diff --git a/plugin/src/main/java/com/flit/protoc/Plugin.java b/plugin/src/main/java/com/flit/protoc/Plugin.java index e238204..9a2986b 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.server.jaxrs.JaxrsGenerator; import com.flit.protoc.gen.server.spring.SpringGenerator; import com.flit.protoc.gen.server.undertow.UndertowGenerator; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; @@ -22,7 +23,7 @@ public Plugin(CodeGeneratorRequest request) { public CodeGeneratorResponse process() { if (!request.hasParameter()) { - return CodeGeneratorResponse.newBuilder().setError("Usage: --flit_out=target=server,type=[spring|undertow]:").build(); + return CodeGeneratorResponse.newBuilder().setError("Usage: --flit_out=target=server,type=[spring|undertow|jaxrs]:").build(); } Map params = Parameter.of(request.getParameter()); @@ -50,6 +51,8 @@ private Generator resolveGenerator(Map params) { return new SpringGenerator(); case "undertow": return new UndertowGenerator(); + case "jaxrs": + return new JaxrsGenerator(); default: throw new GeneratorException("Unknown server type: " + params.get(PARAM_TYPE).getValue()); } diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java new file mode 100644 index 0000000..b538d2c --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java @@ -0,0 +1,16 @@ +package com.flit.protoc.gen.server.jaxrs; + +import com.flit.protoc.gen.server.BaseGenerator; +import com.flit.protoc.gen.server.BaseServerGenerator; +import com.flit.protoc.gen.server.TypeMapper; +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; + +public class JaxrsGenerator extends BaseServerGenerator { + + @Override + protected BaseGenerator getRpcGenerator(FileDescriptorProto proto, ServiceDescriptorProto service, + String context, TypeMapper mapper) { + return new RpcGenerator(proto, service, context, mapper); + } +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java new file mode 100644 index 0000000..48c7182 --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -0,0 +1,85 @@ +package com.flit.protoc.gen.server.jaxrs; + +import com.flit.protoc.gen.server.BaseGenerator; +import com.flit.protoc.gen.server.TypeMapper; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.TypeSpec.Builder; +import java.util.Collections; +import java.util.List; +import javax.lang.model.element.Modifier; + +public class RpcGenerator extends BaseGenerator { + + public static final ClassName PATH = ClassName.bestGuess("javax.ws.rs.Path"); + public static final ClassName POST = ClassName.bestGuess("javax.ws.rs.POST"); + public static final ClassName PRODUCES = ClassName.bestGuess("javax.ws.rs.Produces"); + public static final ClassName CONSUMES = ClassName.bestGuess("javax.ws.rs.Consumes"); + private final String context; + private final Builder rpcResource; + + RpcGenerator(DescriptorProtos.FileDescriptorProto proto, + DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) { + super(proto, service, mapper); + this.context = getContext(context); + this.rpcResource = TypeSpec.classBuilder(getResourceName(service)) + .addModifiers(Modifier.PUBLIC) + .addAnnotation( + AnnotationSpec.builder(PATH).addMember("value", "$S", + this.context + "/" + (proto.hasPackage() ? proto.getPackage() + "." : "") + service + .getName()).build()); + addInstanceFields(); + addConstructor(); + service.getMethodList().forEach(this::addHandleMethod); + } + + private void addConstructor() { + rpcResource.addMethod(MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(getServiceInterface(), "service") + .addStatement("this.service = service").build()); + } + + private void addHandleMethod(MethodDescriptorProto mdp) { + ClassName inputType = mapper.get(mdp.getInputType()); + ClassName outputType = mapper.get(mdp.getOutputType()); + rpcResource.addMethod(MethodSpec.methodBuilder("handle" + mdp.getName()) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(POST) + .addAnnotation(AnnotationSpec.builder(PATH) + .addMember("value", "$S", "/" + mdp.getName()) + .build()) + .addAnnotation(AnnotationSpec.builder(PRODUCES) + .addMember("value", "$S", "application/protobuf") + .addMember("value", "$S", "application/json") + .build()) + .addAnnotation(AnnotationSpec.builder(CONSUMES) + .addMember("value", "$S", "application/protobuf") + .addMember("value", "$S", "application/json") + .build()) + .addParameter(inputType, "request") + .addStatement("return service.handle$L(request)", mdp.getName()) + .returns(outputType) + .build()); + } + + private ClassName getResourceName(DescriptorProtos.ServiceDescriptorProto service) { + return ClassName.get(javaPackage, "Rpc" + service.getName() + "Resource"); + } + + private void addInstanceFields() { + rpcResource.addField(FieldSpec.builder(getServiceInterface(), "service") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()); + } + + @Override + public List getFiles() { + return Collections.singletonList(toFile(getResourceName(service), rpcResource.build())); + } +} diff --git a/plugin/src/test/java/com/flit/protoc/PluginTest.java b/plugin/src/test/java/com/flit/protoc/PluginTest.java index 49499f1..1f627e8 100644 --- a/plugin/src/test/java/com/flit/protoc/PluginTest.java +++ b/plugin/src/test/java/com/flit/protoc/PluginTest.java @@ -14,7 +14,7 @@ public class PluginTest { PluginProtos.CodeGeneratorResponse response = plugin.process(); assertTrue("Expected an error for no parameters", response.hasError()); - assertEquals("Incorrect error message", "Usage: --flit_out=target=server,type=[spring|undertow]:", response.getError()); + assertEquals("Incorrect error message", "Usage: --flit_out=target=server,type=[spring|undertow|jaxrs]:", response.getError()); } @Test public void test_NoTargetSpecified() { diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java new file mode 100644 index 0000000..6de5f88 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java @@ -0,0 +1,67 @@ +package com.flit.protoc.gen.server.jaxrs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.junit.Test; + +/** + * Tests the generation of a service that has core definition imported from another file + */ +public class ContextGeneratorTest extends BaseGeneratorTest { + + @Test + public void test_GenerateWithMissingRoot() throws Exception { + test_Route("context.missing.jaxrs.json", "/twirp/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithEmptyRoot() throws Exception { + test_Route("context.empty.jaxrs.json", "/twirp/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithSlashOnlyRoot() throws Exception { + test_Route("context.slash.jaxrs.json", "/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithSlashRoot() throws Exception { + test_Route("context.root.jaxrs.json", "/root/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithNameRoot() throws Exception { + test_Route("context.name.jaxrs.json", "/fibble/com.example.context.NullService"); + } + + private void test_Route(String file, String route) throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson(file); + + Plugin plugin = new Plugin(request); + PluginProtos.CodeGeneratorResponse response = plugin.process(); + + assertNotNull(response); + assertEquals(2, response.getFileCount()); + + Map files = response.getFileList() + .stream() + .collect(Collectors + .toMap(PluginProtos.CodeGeneratorResponse.File::getName, Function.identity())); + + assertTrue(files.containsKey("com/example/context/rpc/RpcNullService.java")); + assertTrue(files.containsKey("com/example/context/rpc/RpcNullServiceResource.java")); + + assertTrue(files.get("com/example/context/rpc/RpcNullServiceResource.java") + .getContent() + .contains(String.format("@Path(\"%s\")", route))); + } +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java new file mode 100644 index 0000000..139f3d8 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java @@ -0,0 +1,32 @@ +package com.flit.protoc.gen.server.jaxrs; + +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import org.approvaltests.Approvals; +import org.junit.Test; + +public class HelloworldGeneratorTest extends BaseGeneratorTest { + + @Test + public void test_Generate() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.jaxrs.json"); + + Plugin plugin = new Plugin(request); + PluginProtos.CodeGeneratorResponse response = plugin.process(); + + assertNotNull(response); + assertEquals(2, response.getFileCount()); + System.out.println(response.getFile(0).getContent()); + assertEquals(response.getFile(0).getName(), "com/example/helloworld/RpcHelloWorld.java"); + assertEquals(response.getFile(1).getName(), "com/example/helloworld/RpcHelloWorldResource.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); + response.getFileList().forEach(BaseGeneratorTest::assertParses); + } +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt new file mode 100644 index 0000000..f4bac04 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt @@ -0,0 +1,52 @@ +[0] = package com.example.helloworld; + +public interface RpcHelloWorld { + Helloworld.HelloResp handleHello(Helloworld.HelloReq in); + + Helloworld.HelloResp handleHelloAgain(Helloworld.HelloReq in); +} + +[1] = package com.example.helloworld; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +@Path("/twirp/com.example.helloworld.HelloWorld") +public class RpcHelloWorldResource { + private final RpcHelloWorld service; + + public RpcHelloWorldResource(RpcHelloWorld service) { + this.service = service; + } + + @POST + @Path("/Hello") + @Produces({ + "application/protobuf", + "application/json" + }) + @Consumes({ + "application/protobuf", + "application/json" + }) + public Helloworld.HelloResp handleHello(Helloworld.HelloReq request) { + return service.handleHello(request); + } + + @POST + @Path("/HelloAgain") + @Produces({ + "application/protobuf", + "application/json" + }) + @Consumes({ + "application/protobuf", + "application/json" + }) + public Helloworld.HelloResp handleHelloAgain(Helloworld.HelloReq request) { + return service.handleHelloAgain(request); + } +} + diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java new file mode 100644 index 0000000..742db73 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java @@ -0,0 +1,35 @@ +package com.flit.protoc.gen.server.jaxrs; + +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import org.approvaltests.Approvals; +import org.junit.Test; + +/** + * 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.jaxrs.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/RpcStatusResource.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); + response.getFileList().forEach(BaseGeneratorTest::assertParses); + } + +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt new file mode 100644 index 0000000..f756622 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt @@ -0,0 +1,36 @@ +[0] = package com.example.helloworld; + +public interface RpcStatus { + StatusOuterClass.StatusResponse handleGetStatus(Core.Empty in); +} + +[1] = package com.example.helloworld; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +@Path("/twirp/com.example.helloworld.Status") +public class RpcStatusResource { + private final RpcStatus service; + + public RpcStatusResource(RpcStatus service) { + this.service = service; + } + + @POST + @Path("/GetStatus") + @Produces({ + "application/protobuf", + "application/json" + }) + @Consumes({ + "application/protobuf", + "application/json" + }) + public StatusOuterClass.StatusResponse handleGetStatus(Core.Empty request) { + return service.handleGetStatus(request); + } +} + diff --git a/plugin/src/test/resources/context.empty.jaxrs.json b/plugin/src/test/resources/context.empty.jaxrs.json new file mode 100644 index 0000000..a014f19 --- /dev/null +++ b/plugin/src/test/resources/context.empty.jaxrs.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jaxrs,context=", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.missing.jaxrs.json b/plugin/src/test/resources/context.missing.jaxrs.json new file mode 100644 index 0000000..a014f19 --- /dev/null +++ b/plugin/src/test/resources/context.missing.jaxrs.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jaxrs,context=", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.name.jaxrs.json b/plugin/src/test/resources/context.name.jaxrs.json new file mode 100644 index 0000000..ca6e699 --- /dev/null +++ b/plugin/src/test/resources/context.name.jaxrs.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jaxrs,context=fibble", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.root.jaxrs.json b/plugin/src/test/resources/context.root.jaxrs.json new file mode 100644 index 0000000..b8bf8cc --- /dev/null +++ b/plugin/src/test/resources/context.root.jaxrs.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jaxrs,context=/root", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.slash.jaxrs.json b/plugin/src/test/resources/context.slash.jaxrs.json new file mode 100644 index 0000000..ff61342 --- /dev/null +++ b/plugin/src/test/resources/context.slash.jaxrs.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jaxrs,context=/", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/helloworld.jaxrs.json b/plugin/src/test/resources/helloworld.jaxrs.json new file mode 100644 index 0000000..0e5478e --- /dev/null +++ b/plugin/src/test/resources/helloworld.jaxrs.json @@ -0,0 +1,49 @@ +{ + "fileToGenerate": ["helloworld.proto"], + "parameter": "target=server,type=jaxrs", + "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.jaxrs.json b/plugin/src/test/resources/status.jaxrs.json new file mode 100644 index 0000000..130f66a --- /dev/null +++ b/plugin/src/test/resources/status.jaxrs.json @@ -0,0 +1,79 @@ +{ + "fileToGenerate": ["core.proto", "status.proto"], + "parameter": "target=server,type=jaxrs", + "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" + }] +} From 3cd81606e25d2f6f23ee63ab3276e006184198a1 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Wed, 19 Aug 2020 13:01:11 -0700 Subject: [PATCH 09/44] Bump gradle versions --- README.md | 4 +++- plugin/gradle.properties | 2 +- runtime/core/gradle.properties | 2 +- runtime/jaxrs/build.gradle | 15 +++++++++++++++ runtime/jaxrs/gradle.properties | 1 + runtime/spring/gradle.properties | 2 +- runtime/undertow/gradle.properties | 2 +- settings.gradle | 1 + 8 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 runtime/jaxrs/build.gradle create mode 100644 runtime/jaxrs/gradle.properties diff --git a/README.md b/README.md index f7f438f..98402fa 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,13 @@ It supports the generation of Java based servers with the following flavours sup + [Spring Boot/Spring MVC](https://spring.io/projects/spring-boot "Spring Boot") + [Undertow](http://undertow.io/ "Undertow") ++ JAX-RS ([Jersey](https://eclipse-ee4j.github.io/jersey/), [Apache CFX](http://cxf.apache.org/)) ## Building & Running ### Requirements -The build has been tested with [Oracle's JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html "JDK Downloads") (version 1.8) +The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/zulu-community/?architecture=x86-64-bit&package=jdk "JDK Downloads") (version 11) The build uses gradle to generate the artifacts. No installation is required as the project uses the [gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html "gradle wrapper") setup. @@ -27,6 +28,7 @@ The project is split into the following modules: |:------------------|:------------------------------------------------------| | `plugin` | The `protoc` plugin | | `runtime:core` | Core functionality required by generated code | +| `runtime:jaxrs` | Runtime library for JAX-RS servers | | `runtime:spring` | Runtime library for Spring MVC/Boot servers | | `runtime:undertow`| Runtime library for Undertow servers | diff --git a/plugin/gradle.properties b/plugin/gradle.properties index de652db..1bf5794 100644 --- a/plugin/gradle.properties +++ b/plugin/gradle.properties @@ -1 +1 @@ -version=1.1.0 +version=1.2.0 diff --git a/runtime/core/gradle.properties b/runtime/core/gradle.properties index beb72cc..1bf5794 100644 --- a/runtime/core/gradle.properties +++ b/runtime/core/gradle.properties @@ -1 +1 @@ -version=1.0.0 \ No newline at end of file +version=1.2.0 diff --git a/runtime/jaxrs/build.gradle b/runtime/jaxrs/build.gradle new file mode 100644 index 0000000..df77769 --- /dev/null +++ b/runtime/jaxrs/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java' +} + +group 'com.flit' +archivesBaseName = 'flit-jaxrs-runtime' +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/runtime/jaxrs/gradle.properties b/runtime/jaxrs/gradle.properties new file mode 100644 index 0000000..1bf5794 --- /dev/null +++ b/runtime/jaxrs/gradle.properties @@ -0,0 +1 @@ +version=1.2.0 diff --git a/runtime/spring/gradle.properties b/runtime/spring/gradle.properties index beb72cc..1bf5794 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.2.0 diff --git a/runtime/undertow/gradle.properties b/runtime/undertow/gradle.properties index beb72cc..1bf5794 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.2.0 diff --git a/settings.gradle b/settings.gradle index f7e38a2..6865aac 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,5 @@ include "plugin" include 'runtime:core' include 'runtime:spring' include 'runtime:undertow' +include 'runtime:jaxrs' From bac1dfc6b1d5a5bb912264e1cff0811c9c181eb9 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Thu, 20 Aug 2020 14:11:21 -0700 Subject: [PATCH 10/44] Implement JAX-RS flit exception mapper --- runtime/jaxrs/build.gradle | 6 +++ .../runtime/jaxrs/FlitExceptionMapper.java | 38 ++++++++++++++++ .../jaxrs/FlitExceptionMapperTest.java | 43 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java create mode 100644 runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java diff --git a/runtime/jaxrs/build.gradle b/runtime/jaxrs/build.gradle index df77769..86213fe 100644 --- a/runtime/jaxrs/build.gradle +++ b/runtime/jaxrs/build.gradle @@ -11,5 +11,11 @@ repositories { } dependencies { + compile project(':runtime:core') + compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1' + compile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.1.1' + compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'org.glassfish.jersey.core', name: 'jersey-common', version: '2.22.2' + testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' } diff --git a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java new file mode 100644 index 0000000..ec53fd0 --- /dev/null +++ b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java @@ -0,0 +1,38 @@ +package com.flit.runtime.jaxrs; + +import com.flit.runtime.FlitException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FlitExceptionMapper implements ExceptionMapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionMapper.class); + + @Context private HttpServletRequest request; + + @Override + public Response toResponse(FlitException exception) { + LOGGER.error("Flit exception: request = {}, method = {}, code = {}, msg = {}", + request.getRequestURI(), request.getMethod(), exception.getErrorCode(), + exception.getMessage(), exception); + + Map response = new HashMap<>(); + response.put("code", exception.getErrorCode().getErrorCode()); + response.put("msg", exception.getMessage()); + + if (exception.hasMeta()) { + response.put("meta", exception.getMeta()); + } + return Response.status(exception.getErrorCode().getHttpStatus()) + .type(MediaType.APPLICATION_JSON) + .entity(response) + .build(); + } +} diff --git a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java new file mode 100644 index 0000000..97ce839 --- /dev/null +++ b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java @@ -0,0 +1,43 @@ +package com.flit.runtime.jaxrs; + +import static org.junit.Assert.assertEquals; + +import com.flit.runtime.ErrorCode; +import com.flit.runtime.FlitException; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class FlitExceptionMapperTest { + + @Mock + private HttpServletRequest request; + + @InjectMocks + private FlitExceptionMapper flitExceptionMapper = new FlitExceptionMapper(); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testToResponse() { + FlitException flit = FlitException.builder() + .withCause(new RuntimeException("Failed")) + .withErrorCode(ErrorCode.INTERNAL) + .withMessage("with this message") + .build(); + Response response = flitExceptionMapper.toResponse(flit); + assertEquals(response.getStatus(), 500); + Map expectedResult = Map.of("msg", "with this message", "code", "internal"); + assertEquals(response.getEntity(), expectedResult); + assertEquals(response.getMediaType(), MediaType.APPLICATION_JSON_TYPE); + } +} From b3fbf0b978197b11372724da87d6c851d07fbe02 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Thu, 20 Aug 2020 16:06:05 -0700 Subject: [PATCH 11/44] Wrap protobuf in Response object --- .../protoc/gen/server/jaxrs/RpcGenerator.java | 8 ++++---- .../server/jaxrs/HelloworldGeneratorTest.java | 1 - ...orldGeneratorTest.test_Generate.approved.txt | 17 +++++++++-------- ...atusGeneratorTest.test_Generate.approved.txt | 9 +++++---- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index 48c7182..0fa5227 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -56,16 +56,16 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .addMember("value", "$S", "/" + mdp.getName()) .build()) .addAnnotation(AnnotationSpec.builder(PRODUCES) - .addMember("value", "$S", "application/protobuf") + .addMember("value", "$S", "application/x-protobuf") .addMember("value", "$S", "application/json") .build()) .addAnnotation(AnnotationSpec.builder(CONSUMES) - .addMember("value", "$S", "application/protobuf") + .addMember("value", "$S", "application/x-protobuf") .addMember("value", "$S", "application/json") .build()) .addParameter(inputType, "request") - .addStatement("return service.handle$L(request)", mdp.getName()) - .returns(outputType) + .addStatement("return Response.ok(service.handle$L(request)).build()", mdp.getName()) + .returns(ClassName.bestGuess("javax.ws.rs.core.Response")) .build()); } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java index 139f3d8..fa0a5d5 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java @@ -22,7 +22,6 @@ public void test_Generate() throws Exception { assertNotNull(response); assertEquals(2, response.getFileCount()); - System.out.println(response.getFile(0).getContent()); assertEquals(response.getFile(0).getName(), "com/example/helloworld/RpcHelloWorld.java"); assertEquals(response.getFile(1).getName(), "com/example/helloworld/RpcHelloWorldResource.java"); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt index f4bac04..0647b0a 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt @@ -12,6 +12,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; @Path("/twirp/com.example.helloworld.HelloWorld") public class RpcHelloWorldResource { @@ -24,29 +25,29 @@ public class RpcHelloWorldResource { @POST @Path("/Hello") @Produces({ - "application/protobuf", + "application/x-protobuf", "application/json" }) @Consumes({ - "application/protobuf", + "application/x-protobuf", "application/json" }) - public Helloworld.HelloResp handleHello(Helloworld.HelloReq request) { - return service.handleHello(request); + public Response handleHello(Helloworld.HelloReq request) { + return Response.ok(service.handleHello(request)).build(); } @POST @Path("/HelloAgain") @Produces({ - "application/protobuf", + "application/x-protobuf", "application/json" }) @Consumes({ - "application/protobuf", + "application/x-protobuf", "application/json" }) - public Helloworld.HelloResp handleHelloAgain(Helloworld.HelloReq request) { - return service.handleHelloAgain(request); + public Response handleHelloAgain(Helloworld.HelloReq request) { + return Response.ok(service.handleHelloAgain(request)).build(); } } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt index f756622..08075ff 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt @@ -10,6 +10,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; @Path("/twirp/com.example.helloworld.Status") public class RpcStatusResource { @@ -22,15 +23,15 @@ public class RpcStatusResource { @POST @Path("/GetStatus") @Produces({ - "application/protobuf", + "application/x-protobuf", "application/json" }) @Consumes({ - "application/protobuf", + "application/x-protobuf", "application/json" }) - public StatusOuterClass.StatusResponse handleGetStatus(Core.Empty request) { - return service.handleGetStatus(request); + public Response handleGetStatus(Core.Empty request) { + return Response.ok(service.handleGetStatus(request)).build(); } } From ec321b67e1a38f0064a954e7eea3196e4c5b1c13 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Thu, 20 Aug 2020 16:06:28 -0700 Subject: [PATCH 12/44] Add protobuf message provider --- .../runtime/jaxrs/FlitExceptionMapper.java | 2 + .../jaxrs/InvalidProtobufExceptionMapper.java | 36 +++++++ .../jaxrs/ProtobufMessageProvider.java | 97 +++++++++++++++++++ .../InvalidProtobufExceptionMapperTest.java | 40 ++++++++ 4 files changed, 175 insertions(+) create mode 100644 runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapper.java create mode 100644 runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java create mode 100644 runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java diff --git a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java index ec53fd0..6687229 100644 --- a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java +++ b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/FlitExceptionMapper.java @@ -8,9 +8,11 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@Provider public class FlitExceptionMapper implements ExceptionMapper { private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionMapper.class); diff --git a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapper.java b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapper.java new file mode 100644 index 0000000..75f6011 --- /dev/null +++ b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapper.java @@ -0,0 +1,36 @@ +package com.flit.runtime.jaxrs; + +import com.flit.runtime.ErrorCode; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InvalidProtobufExceptionMapper implements + ExceptionMapper { + + private static final Logger LOGGER = LoggerFactory + .getLogger(InvalidProtobufExceptionMapper.class); + + @Context private HttpServletRequest request; + + @Override + public Response toResponse(InvalidProtocolBufferException exception) { + LOGGER.error("InvalidProtocolBufferException: request = {}, method = {}, msg= {}", + request.getRequestURI(), request.getMethod(), exception.getMessage(), exception); + + Map response = new HashMap<>(); + response.put("code", ErrorCode.INVALID_ARGUMENT.getErrorCode()); + response.put("msg", exception.getMessage()); + return Response.status(ErrorCode.INVALID_ARGUMENT.getHttpStatus()) + .type(MediaType.APPLICATION_JSON) + .entity(response) + .build(); + } +} diff --git a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java new file mode 100644 index 0000000..90c4a85 --- /dev/null +++ b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java @@ -0,0 +1,97 @@ +package com.flit.runtime.jaxrs; + +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +@Provider +public class ProtobufMessageProvider implements MessageBodyWriter, + MessageBodyReader { + + @Override + public boolean isWriteable(Class type, Type genericType, + Annotation[] annotations, MediaType mediaType) { + return Message.class.isAssignableFrom(type) && ( + "json".equals(mediaType.getSubtype()) || "protobuf".equals(mediaType.getSubtype())); + } + + @Override + public long getSize(Message t, Class type, + Type genericType, Annotation[] annotations, + MediaType mediaType) { + if (t == null) { + return -1; + } + ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + try { + writeTo(t, type, genericType, annotations, mediaType, null, out); + } catch (java.io.IOException e) { + return -1; + } + return out.size(); + } + + @Override + public void writeTo(Message t, Class type, + Type genericType, Annotation[] annotations, + MediaType mediaType, + MultivaluedMap httpHeaders, + OutputStream entityStream) + throws IOException, WebApplicationException { + switch (mediaType.getSubtype()) { + case "protobuf": + t.writeTo(entityStream); + break; + case "json": + entityStream + .write(JsonFormat.printer().print(t).getBytes(StandardCharsets.UTF_8)); + break; + default: + throw new WebApplicationException("MediaType not supported!"); + } + } + + @Override + public boolean isReadable(Class type, Type genericType, + Annotation[] annotations, MediaType mediaType) { + return Message.class.isAssignableFrom(type) && ( + "json".equals(mediaType.getSubtype()) || "protobuf".equals(mediaType.getSubtype())); + } + + @Override + public Message readFrom(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) + throws WebApplicationException { + try { + switch (mediaType.getSubtype()) { + case "protobuf": + Method m = type.getMethod("parseFrom", InputStream.class); + return (Message) m.invoke(null, entityStream); + case "json": + Message.Builder msg = (Message.Builder) type + .getMethod("newBuilder").invoke(null); + JsonFormat.parser() + .merge(new InputStreamReader(entityStream), msg); + return msg.build(); + default: + throw new WebApplicationException("MediaType not supported!"); + } + } catch (Exception e) { + throw new WebApplicationException(e); + } + } +} diff --git a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java new file mode 100644 index 0000000..9b13952 --- /dev/null +++ b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java @@ -0,0 +1,40 @@ +package com.flit.runtime.jaxrs; + +import static org.junit.Assert.assertEquals; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class InvalidProtobufExceptionMapperTest { + + @Mock + private HttpServletRequest request; + + @InjectMocks + private InvalidProtobufExceptionMapper mapper = new InvalidProtobufExceptionMapper(); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testToResponse() { + InvalidProtocolBufferException exception = new InvalidProtocolBufferException( + "something happened"); + Response response = mapper.toResponse(exception); + assertEquals(response.getStatus(), 400); + Map expectedResult = Map + .of("msg", "something happened", "code", "invalid_argument"); + assertEquals(response.getEntity(), expectedResult); + assertEquals(response.getMediaType(), MediaType.APPLICATION_JSON_TYPE); + } +} From ecaafc1ea1cb2d8d767baa8fbfae51697141f336 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Fri, 21 Aug 2020 11:52:21 -0700 Subject: [PATCH 13/44] Update Readme with jaxrs option --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 98402fa..b035805 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,11 @@ The plugin is executed as part of a protoc compilation step: 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 | -| `type` | Y | `enum[spring,undertow,boot]` | Type of target to generate | -| `context` | N | `string` | Base context for routing, default is `/twirp` | +| Name | Required | Type | Description | +|:--------------|:---------:|:----------------------------------|:----------------------------------------------------------| +| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc | +| `type` | Y | `enum[spring,undertow,boot,jaxrs]`| Type of target to generate | +| `context` | N | `string` | Base context for routing, default is `/twirp` | # Development From da63de45c5fcdb6bb38148ffd56309803dc43afd Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Fri, 21 Aug 2020 11:52:49 -0700 Subject: [PATCH 14/44] Use consistent protobuf media type --- .../com/flit/protoc/gen/server/jaxrs/RpcGenerator.java | 5 ++--- .../HelloworldGeneratorTest.test_Generate.approved.txt | 8 ++++---- .../jaxrs/StatusGeneratorTest.test_Generate.approved.txt | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index 0fa5227..8857975 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -48,7 +48,6 @@ private void addConstructor() { private void addHandleMethod(MethodDescriptorProto mdp) { ClassName inputType = mapper.get(mdp.getInputType()); - ClassName outputType = mapper.get(mdp.getOutputType()); rpcResource.addMethod(MethodSpec.methodBuilder("handle" + mdp.getName()) .addModifiers(Modifier.PUBLIC) .addAnnotation(POST) @@ -56,11 +55,11 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .addMember("value", "$S", "/" + mdp.getName()) .build()) .addAnnotation(AnnotationSpec.builder(PRODUCES) - .addMember("value", "$S", "application/x-protobuf") + .addMember("value", "$S", "application/protobuf") .addMember("value", "$S", "application/json") .build()) .addAnnotation(AnnotationSpec.builder(CONSUMES) - .addMember("value", "$S", "application/x-protobuf") + .addMember("value", "$S", "application/protobuf") .addMember("value", "$S", "application/json") .build()) .addParameter(inputType, "request") diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt index 0647b0a..abbb8ed 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt @@ -25,11 +25,11 @@ public class RpcHelloWorldResource { @POST @Path("/Hello") @Produces({ - "application/x-protobuf", + "application/protobuf", "application/json" }) @Consumes({ - "application/x-protobuf", + "application/protobuf", "application/json" }) public Response handleHello(Helloworld.HelloReq request) { @@ -39,11 +39,11 @@ public class RpcHelloWorldResource { @POST @Path("/HelloAgain") @Produces({ - "application/x-protobuf", + "application/protobuf", "application/json" }) @Consumes({ - "application/x-protobuf", + "application/protobuf", "application/json" }) public Response handleHelloAgain(Helloworld.HelloReq request) { diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt index 08075ff..dafa55a 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt @@ -23,11 +23,11 @@ public class RpcStatusResource { @POST @Path("/GetStatus") @Produces({ - "application/x-protobuf", + "application/protobuf", "application/json" }) @Consumes({ - "application/x-protobuf", + "application/protobuf", "application/json" }) public Response handleGetStatus(Core.Empty request) { From 43f8b7875a3c0221aad91c912a22941df01949c3 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Fri, 21 Aug 2020 12:21:26 -0700 Subject: [PATCH 15/44] Refactor media type to use guava's MediaType constants --- .../com/flit/protoc/gen/server/jaxrs/RpcGenerator.java | 9 +++++---- .../com/flit/protoc/gen/server/spring/RpcGenerator.java | 7 ++++--- .../flit/protoc/gen/server/undertow/RpcGenerator.java | 7 ++++--- .../HelloworldGeneratorTest.test_Generate.approved.txt | 8 ++++---- .../jaxrs/StatusGeneratorTest.test_Generate.approved.txt | 4 ++-- .../HelloworldGeneratorTest.test_Generate.approved.txt | 4 ++-- .../StatusGeneratorTest.test_Generate.approved.txt | 2 +- .../HelloworldGeneratorTest.test_Generate.approved.txt | 4 ++-- .../StatusGeneratorTest.test_Generate.approved.txt | 2 +- 9 files changed, 25 insertions(+), 22 deletions(-) diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index 8857975..afcf955 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -2,6 +2,7 @@ import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.TypeMapper; +import com.google.common.net.MediaType; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; @@ -55,12 +56,12 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .addMember("value", "$S", "/" + mdp.getName()) .build()) .addAnnotation(AnnotationSpec.builder(PRODUCES) - .addMember("value", "$S", "application/protobuf") - .addMember("value", "$S", "application/json") + .addMember("value", "$S", MediaType.PROTOBUF.toString()) + .addMember("value", "$S", MediaType.JSON_UTF_8.toString()) .build()) .addAnnotation(AnnotationSpec.builder(CONSUMES) - .addMember("value", "$S", "application/protobuf") - .addMember("value", "$S", "application/json") + .addMember("value", "$S", MediaType.PROTOBUF.toString()) + .addMember("value", "$S", MediaType.JSON_UTF_8.toString()) .build()) .addParameter(inputType, "request") .addStatement("return Response.ok(service.handle$L(request)).build()", mdp.getName()) 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..b73d07b 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 @@ -3,6 +3,7 @@ import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.TypeMapper; import com.flit.protoc.gen.server.Types; +import com.google.common.net.MediaType; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; import com.squareup.javapoet.*; @@ -47,7 +48,7 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addAnnotation(AnnotationSpec.builder(PostMapping).addMember("value", "$S", route).build()) .addStatement("boolean json = false") .addStatement("final $T data", inputType) - .beginControlFlow("if (request.getContentType().equals($S))", "application/protobuf") + .beginControlFlow("if (request.getContentType().equals($S))", MediaType.PROTOBUF.toString()) .addStatement("data = $T.parseFrom(request.getInputStream())", inputType) .nextControlFlow("else if (request.getContentType().startsWith($S))", "application/json") .addStatement("json = true") @@ -66,12 +67,12 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addStatement("response.setStatus(200)") // send the response .beginControlFlow("if (json)") - .addStatement("response.setContentType($S)", "application/json;charset=UTF-8") + .addStatement("response.setContentType($S)", MediaType.JSON_UTF_8.toString()) .addStatement("response.getOutputStream().write($T.printer().omittingInsignificantWhitespace().print(retval).getBytes($T.UTF_8))", Types.JsonFormat, Types.StandardCharsets) .nextControlFlow("else") - .addStatement("response.setContentType($S)", "application/protobuf") + .addStatement("response.setContentType($S)", MediaType.PROTOBUF.toString()) .addStatement("retval.writeTo(response.getOutputStream())") .endControlFlow() .build()); 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..4fb4951 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 @@ -3,6 +3,7 @@ import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.TypeMapper; import com.flit.protoc.gen.server.Types; +import com.google.common.net.MediaType; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; import com.squareup.javapoet.*; @@ -110,7 +111,7 @@ private void writeHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addStatement("boolean json = false") .addStatement("final $T data", inputType) .addStatement("final String contentType = exchange.getRequestHeaders().get($T.CONTENT_TYPE).getFirst()", Headers) - .beginControlFlow("if (contentType.equals($S))", "application/protobuf") + .beginControlFlow("if (contentType.equals($S))", MediaType.PROTOBUF.toString()) .addStatement("data = $T.parseFrom(exchange.getInputStream())", inputType) .nextControlFlow("else if (contentType.startsWith($S))", "application/json") .addStatement("json = true") @@ -126,10 +127,10 @@ private void writeHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addStatement("exchange.setStatusCode(200)") // put the result on the wire .beginControlFlow("if (json)") - .addStatement("exchange.getResponseHeaders().put($T.CONTENT_TYPE, $S)", Headers, "application/json;charset=UTF-8") + .addStatement("exchange.getResponseHeaders().put($T.CONTENT_TYPE, $S)", Headers, MediaType.JSON_UTF_8.toString()) .addStatement("exchange.getResponseSender().send($T.printer().omittingInsignificantWhitespace().print(response))", JsonFormat) .nextControlFlow("else") - .addStatement("exchange.getResponseHeaders().put($T.CONTENT_TYPE, $S)", Headers, "application/protobuf") + .addStatement("exchange.getResponseHeaders().put($T.CONTENT_TYPE, $S)", Headers, MediaType.PROTOBUF.toString()) .addStatement("response.writeTo(exchange.getOutputStream())") .endControlFlow() .build()); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt index abbb8ed..f86953e 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt @@ -26,11 +26,11 @@ public class RpcHelloWorldResource { @Path("/Hello") @Produces({ "application/protobuf", - "application/json" + "application/json; charset=utf-8" }) @Consumes({ "application/protobuf", - "application/json" + "application/json; charset=utf-8" }) public Response handleHello(Helloworld.HelloReq request) { return Response.ok(service.handleHello(request)).build(); @@ -40,11 +40,11 @@ public class RpcHelloWorldResource { @Path("/HelloAgain") @Produces({ "application/protobuf", - "application/json" + "application/json; charset=utf-8" }) @Consumes({ "application/protobuf", - "application/json" + "application/json; charset=utf-8" }) public Response handleHelloAgain(Helloworld.HelloReq request) { return Response.ok(service.handleHelloAgain(request)).build(); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt index dafa55a..72348f3 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt @@ -24,11 +24,11 @@ public class RpcStatusResource { @Path("/GetStatus") @Produces({ "application/protobuf", - "application/json" + "application/json; charset=utf-8" }) @Consumes({ "application/protobuf", - "application/json" + "application/json; charset=utf-8" }) public Response handleGetStatus(Core.Empty request) { return Response.ok(service.handleGetStatus(request)).build(); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt index 273bcad..4af39fd 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt @@ -42,7 +42,7 @@ public class RpcHelloWorldController { Helloworld.HelloResp retval = service.handleHello(data); response.setStatus(200); if (json) { - response.setContentType("application/json;charset=UTF-8"); + response.setContentType("application/json; charset=utf-8"); response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); } else { response.setContentType("application/protobuf"); @@ -69,7 +69,7 @@ public class RpcHelloWorldController { Helloworld.HelloResp retval = service.handleHelloAgain(data); response.setStatus(200); if (json) { - response.setContentType("application/json;charset=UTF-8"); + response.setContentType("application/json; charset=utf-8"); response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); } else { response.setContentType("application/protobuf"); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt index de9ed27..c5d6a51 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt @@ -40,7 +40,7 @@ public class RpcStatusController { StatusOuterClass.StatusResponse retval = service.handleGetStatus(data); response.setStatus(200); if (json) { - response.setContentType("application/json;charset=UTF-8"); + response.setContentType("application/json; charset=utf-8"); response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); } else { response.setContentType("application/protobuf"); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt index e286593..51361d0 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt @@ -78,7 +78,7 @@ public class RpcHelloWorldHandler implements HttpHandler { Helloworld.HelloResp response = service.handleHello(data); exchange.setStatusCode(200); if (json) { - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json;charset=UTF-8"); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json; charset=utf-8"); exchange.getResponseSender().send(JsonFormat.printer().omittingInsignificantWhitespace().print(response)); } else { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/protobuf"); @@ -104,7 +104,7 @@ public class RpcHelloWorldHandler implements HttpHandler { Helloworld.HelloResp response = service.handleHelloAgain(data); exchange.setStatusCode(200); if (json) { - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json;charset=UTF-8"); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json; charset=utf-8"); exchange.getResponseSender().send(JsonFormat.printer().omittingInsignificantWhitespace().print(response)); } else { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/protobuf"); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt index 65f5b4d..884a706 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt @@ -75,7 +75,7 @@ public class RpcStatusHandler implements HttpHandler { StatusOuterClass.StatusResponse response = service.handleGetStatus(data); exchange.setStatusCode(200); if (json) { - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json;charset=UTF-8"); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json; charset=utf-8"); exchange.getResponseSender().send(JsonFormat.printer().omittingInsignificantWhitespace().print(response)); } else { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/protobuf"); From 3e6588349383c7ba4f86e0f9bcd19dc14cba5727 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Fri, 21 Aug 2020 12:26:04 -0700 Subject: [PATCH 16/44] Extract to constants --- .../jaxrs/ProtobufMessageProvider.java | 21 +++++++++++-------- .../jaxrs/FlitExceptionMapperTest.java | 3 ++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java index 90c4a85..95039cc 100644 --- a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java +++ b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java @@ -22,23 +22,26 @@ public class ProtobufMessageProvider implements MessageBodyWriter, MessageBodyReader { + private static final String JSON = "json"; + private static final String PROTOBUF = "protobuf"; + @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return Message.class.isAssignableFrom(type) && ( - "json".equals(mediaType.getSubtype()) || "protobuf".equals(mediaType.getSubtype())); + JSON.equals(mediaType.getSubtype()) || PROTOBUF.equals(mediaType.getSubtype())); } @Override - public long getSize(Message t, Class type, + public long getSize(Message message, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - if (t == null) { + if (message == null) { return -1; } ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); try { - writeTo(t, type, genericType, annotations, mediaType, null, out); + writeTo(message, type, genericType, annotations, mediaType, null, out); } catch (java.io.IOException e) { return -1; } @@ -53,10 +56,10 @@ public void writeTo(Message t, Class type, OutputStream entityStream) throws IOException, WebApplicationException { switch (mediaType.getSubtype()) { - case "protobuf": + case PROTOBUF: t.writeTo(entityStream); break; - case "json": + case JSON: entityStream .write(JsonFormat.printer().print(t).getBytes(StandardCharsets.UTF_8)); break; @@ -69,7 +72,7 @@ public void writeTo(Message t, Class type, public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return Message.class.isAssignableFrom(type) && ( - "json".equals(mediaType.getSubtype()) || "protobuf".equals(mediaType.getSubtype())); + JSON.equals(mediaType.getSubtype()) || PROTOBUF.equals(mediaType.getSubtype())); } @Override @@ -78,10 +81,10 @@ public Message readFrom(Class type, Type genericType, Annotation[] anno throws WebApplicationException { try { switch (mediaType.getSubtype()) { - case "protobuf": + case PROTOBUF: Method m = type.getMethod("parseFrom", InputStream.class); return (Message) m.invoke(null, entityStream); - case "json": + case JSON: Message.Builder msg = (Message.Builder) type .getMethod("newBuilder").invoke(null); JsonFormat.parser() diff --git a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java index 97ce839..4cd440f 100644 --- a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java +++ b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java @@ -8,6 +8,7 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; @@ -35,7 +36,7 @@ public void testToResponse() { .withMessage("with this message") .build(); Response response = flitExceptionMapper.toResponse(flit); - assertEquals(response.getStatus(), 500); + assertEquals(response.getStatus(), Status.INTERNAL_SERVER_ERROR.getStatusCode()); Map expectedResult = Map.of("msg", "with this message", "code", "internal"); assertEquals(response.getEntity(), expectedResult); assertEquals(response.getMediaType(), MediaType.APPLICATION_JSON_TYPE); From b2678a59109abded6fe70b669a0feb55cda99538 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Fri, 21 Aug 2020 12:30:46 -0700 Subject: [PATCH 17/44] Create gradle.yml --- .github/workflows/gradle.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..d131978 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,26 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.11 + uses: actions/setup-java@v1 + with: + java-version: 1.11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew clean build From 13211d642b2e1f906205935d5c6436e160d66ca9 Mon Sep 17 00:00:00 2001 From: Javier Li Sam Date: Mon, 24 Aug 2020 18:25:33 -0700 Subject: [PATCH 18/44] Simplify jax-rs resource (removes the protobuf message provider) --- .../protoc/gen/server/jaxrs/RpcGenerator.java | 53 ++++++++-- ...ldGeneratorTest.test_Generate.approved.txt | 82 ++++++++++---- ...usGeneratorTest.test_Generate.approved.txt | 46 +++++--- .../jaxrs/ProtobufMessageProvider.java | 100 ------------------ 4 files changed, 134 insertions(+), 147 deletions(-) delete mode 100644 runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index afcf955..dd75ded 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -2,6 +2,7 @@ import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.TypeMapper; +import com.flit.protoc.gen.server.Types; import com.google.common.net.MediaType; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; @@ -10,6 +11,7 @@ import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeSpec.Builder; import java.util.Collections; @@ -22,6 +24,9 @@ public class RpcGenerator extends BaseGenerator { public static final ClassName POST = ClassName.bestGuess("javax.ws.rs.POST"); public static final ClassName PRODUCES = ClassName.bestGuess("javax.ws.rs.Produces"); public static final ClassName CONSUMES = ClassName.bestGuess("javax.ws.rs.Consumes"); + public static final ClassName CONTEXT = ClassName.bestGuess("javax.ws.rs.core.Context"); + public static final ClassName HttpServletRequest = ClassName.bestGuess("javax.servlet.http.HttpServletRequest"); + public static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); private final String context; private final Builder rpcResource; @@ -49,23 +54,49 @@ private void addConstructor() { private void addHandleMethod(MethodDescriptorProto mdp) { ClassName inputType = mapper.get(mdp.getInputType()); + ClassName outputType = mapper.get(mdp.getOutputType()); rpcResource.addMethod(MethodSpec.methodBuilder("handle" + mdp.getName()) .addModifiers(Modifier.PUBLIC) .addAnnotation(POST) .addAnnotation(AnnotationSpec.builder(PATH) .addMember("value", "$S", "/" + mdp.getName()) .build()) - .addAnnotation(AnnotationSpec.builder(PRODUCES) - .addMember("value", "$S", MediaType.PROTOBUF.toString()) - .addMember("value", "$S", MediaType.JSON_UTF_8.toString()) - .build()) - .addAnnotation(AnnotationSpec.builder(CONSUMES) - .addMember("value", "$S", MediaType.PROTOBUF.toString()) - .addMember("value", "$S", MediaType.JSON_UTF_8.toString()) - .build()) - .addParameter(inputType, "request") - .addStatement("return Response.ok(service.handle$L(request)).build()", mdp.getName()) - .returns(ClassName.bestGuess("javax.ws.rs.core.Response")) + .addParameter(ParameterSpec.builder(HttpServletRequest, "request") + .addAnnotation(CONTEXT).build()) + .addParameter(ParameterSpec.builder(HttpServletResponse, "response") + .addAnnotation(CONTEXT).build()) + .addException(Types.Exception) + .addStatement("boolean json = false") + .addStatement("final $T data", inputType) + .beginControlFlow("if (request.getContentType().equals($S))", MediaType.PROTOBUF.toString()) + .addStatement("data = $T.parseFrom(request.getInputStream())", inputType) + .nextControlFlow("else if (request.getContentType().startsWith($S))", "application/json") + .addStatement("json = true") + .addStatement("$T.Builder builder = $T.newBuilder()", inputType, inputType) + .addStatement("$T.parser().merge(new $T(request.getInputStream(), $T.UTF_8), builder)", + Types.JsonFormat, + Types.InputStreamReader, + Types.StandardCharsets) + .addStatement("data = builder.build()") + .nextControlFlow("else") + .addStatement("response.setStatus(415)") + .addStatement("response.flushBuffer()") + .addStatement("return") + .endControlFlow() + // route to the service + .addStatement("$T retval = service.handle$L(data)", outputType, mdp.getName()) + .addStatement("response.setStatus(200)") + // send the response + .beginControlFlow("if (json)") + .addStatement("response.setContentType($S)", MediaType.JSON_UTF_8.toString()) + .addStatement("response.getOutputStream().write($T.printer().omittingInsignificantWhitespace().print(retval).getBytes($T.UTF_8))", + Types.JsonFormat, + Types.StandardCharsets) + .nextControlFlow("else") + .addStatement("response.setContentType($S)", MediaType.PROTOBUF.toString()) + .addStatement("retval.writeTo(response.getOutputStream())") + .endControlFlow() + .addStatement("response.flushBuffer()") .build()); } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt index f86953e..a76f26e 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt @@ -8,11 +8,15 @@ public interface RpcHelloWorld { [1] = package com.example.helloworld; -import javax.ws.rs.Consumes; +import com.google.protobuf.util.JsonFormat; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.POST; import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Response; +import javax.ws.rs.core.Context; @Path("/twirp/com.example.helloworld.HelloWorld") public class RpcHelloWorldResource { @@ -24,30 +28,62 @@ public class RpcHelloWorldResource { @POST @Path("/Hello") - @Produces({ - "application/protobuf", - "application/json; charset=utf-8" - }) - @Consumes({ - "application/protobuf", - "application/json; charset=utf-8" - }) - public Response handleHello(Helloworld.HelloReq request) { - return Response.ok(service.handleHello(request)).build(); + public void handleHello(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHello(data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); } @POST @Path("/HelloAgain") - @Produces({ - "application/protobuf", - "application/json; charset=utf-8" - }) - @Consumes({ - "application/protobuf", - "application/json; charset=utf-8" - }) - public Response handleHelloAgain(Helloworld.HelloReq request) { - return Response.ok(service.handleHelloAgain(request)).build(); + public void handleHelloAgain(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHelloAgain(data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); } } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt index 72348f3..a466727 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt @@ -6,11 +6,15 @@ public interface RpcStatus { [1] = package com.example.helloworld; -import javax.ws.rs.Consumes; +import com.google.protobuf.util.JsonFormat; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.POST; import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Response; +import javax.ws.rs.core.Context; @Path("/twirp/com.example.helloworld.Status") public class RpcStatusResource { @@ -22,16 +26,32 @@ public class RpcStatusResource { @POST @Path("/GetStatus") - @Produces({ - "application/protobuf", - "application/json; charset=utf-8" - }) - @Consumes({ - "application/protobuf", - "application/json; charset=utf-8" - }) - public Response handleGetStatus(Core.Empty request) { - return Response.ok(service.handleGetStatus(request)).build(); + public void handleGetStatus(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Core.Empty data; + if (request.getContentType().equals("application/protobuf")) { + data = Core.Empty.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Core.Empty.Builder builder = Core.Empty.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + StatusOuterClass.StatusResponse retval = service.handleGetStatus(data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); } } diff --git a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java b/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java deleted file mode 100644 index 95039cc..0000000 --- a/runtime/jaxrs/src/main/java/com/flit/runtime/jaxrs/ProtobufMessageProvider.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.flit.runtime.jaxrs; - -import com.google.protobuf.Message; -import com.google.protobuf.util.JsonFormat; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyReader; -import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.Provider; - -@Provider -public class ProtobufMessageProvider implements MessageBodyWriter, - MessageBodyReader { - - private static final String JSON = "json"; - private static final String PROTOBUF = "protobuf"; - - @Override - public boolean isWriteable(Class type, Type genericType, - Annotation[] annotations, MediaType mediaType) { - return Message.class.isAssignableFrom(type) && ( - JSON.equals(mediaType.getSubtype()) || PROTOBUF.equals(mediaType.getSubtype())); - } - - @Override - public long getSize(Message message, Class type, - Type genericType, Annotation[] annotations, - MediaType mediaType) { - if (message == null) { - return -1; - } - ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); - try { - writeTo(message, type, genericType, annotations, mediaType, null, out); - } catch (java.io.IOException e) { - return -1; - } - return out.size(); - } - - @Override - public void writeTo(Message t, Class type, - Type genericType, Annotation[] annotations, - MediaType mediaType, - MultivaluedMap httpHeaders, - OutputStream entityStream) - throws IOException, WebApplicationException { - switch (mediaType.getSubtype()) { - case PROTOBUF: - t.writeTo(entityStream); - break; - case JSON: - entityStream - .write(JsonFormat.printer().print(t).getBytes(StandardCharsets.UTF_8)); - break; - default: - throw new WebApplicationException("MediaType not supported!"); - } - } - - @Override - public boolean isReadable(Class type, Type genericType, - Annotation[] annotations, MediaType mediaType) { - return Message.class.isAssignableFrom(type) && ( - JSON.equals(mediaType.getSubtype()) || PROTOBUF.equals(mediaType.getSubtype())); - } - - @Override - public Message readFrom(Class type, Type genericType, Annotation[] annotations, - MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) - throws WebApplicationException { - try { - switch (mediaType.getSubtype()) { - case PROTOBUF: - Method m = type.getMethod("parseFrom", InputStream.class); - return (Message) m.invoke(null, entityStream); - case JSON: - Message.Builder msg = (Message.Builder) type - .getMethod("newBuilder").invoke(null); - JsonFormat.parser() - .merge(new InputStreamReader(entityStream), msg); - return msg.build(); - default: - throw new WebApplicationException("MediaType not supported!"); - } - } catch (Exception e) { - throw new WebApplicationException(e); - } - } -} From 3a4eece89461ae432f242125940436ff2ad5c78b Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Mon, 14 Nov 2022 10:56:33 -0800 Subject: [PATCH 19/44] Optionally pass request to service interface (#7) --- README.md | 11 +- plugin/gradle.properties | 2 +- .../main/java/com/flit/protoc/Parameter.java | 10 +- .../src/main/java/com/flit/protoc/Plugin.java | 31 +++-- .../gen/server/BaseServerGenerator.java | 20 ++- .../protoc/gen/server/ServiceGenerator.java | 28 +++-- .../gen/server/jaxrs/JaxrsGenerator.java | 15 ++- .../protoc/gen/server/jaxrs/RpcGenerator.java | 21 +++- .../gen/server/spring/RpcGenerator.java | 19 ++- .../gen/server/spring/SpringGenerator.java | 17 ++- .../gen/server/undertow/RpcGenerator.java | 51 ++++++-- .../server/undertow/UndertowGenerator.java | 17 ++- .../test/java/com/flit/protoc/PluginTest.java | 8 +- .../flit/protoc/gen/BaseGeneratorTest.java | 14 ++- .../server/jaxrs/HelloworldGeneratorTest.java | 16 +++ ...Test.test_GenerateWithRequest.approved.txt | 91 ++++++++++++++ .../spring/HelloworldGeneratorTest.java | 23 +++- ...Test.test_GenerateWithRequest.approved.txt | 82 ++++++++++++ .../undertow/HelloworldGeneratorTest.java | 23 +++- ...Test.test_GenerateWithRequest.approved.txt | 117 ++++++++++++++++++ runtime/undertow/build.gradle | 6 +- 21 files changed, 554 insertions(+), 68 deletions(-) create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt diff --git a/README.md b/README.md index b035805..5d938e1 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,12 @@ The plugin is executed as part of a protoc compilation step: 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 | -| `type` | Y | `enum[spring,undertow,boot,jaxrs]`| Type of target to generate | -| `context` | N | `string` | Base context for routing, default is `/twirp` | +| Name | Required | Type | Description | +|:----------|:---------:|:----------------------------------|:-------------------------------------------------------| +| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc | +| `type` | Y | `enum[spring,undertow,boot,jaxrs]`| Type of target to generate | +| `context` | N | `string` | Base context for routing, default is `/twirp` | +| `request` | N | `string` | If the request parameter should pass to the service | # Development diff --git a/plugin/gradle.properties b/plugin/gradle.properties index 1bf5794..2d48fba 100644 --- a/plugin/gradle.properties +++ b/plugin/gradle.properties @@ -1 +1 @@ -version=1.2.0 +version=1.2.1 diff --git a/plugin/src/main/java/com/flit/protoc/Parameter.java b/plugin/src/main/java/com/flit/protoc/Parameter.java index 0b573f7..aa70b7c 100644 --- a/plugin/src/main/java/com/flit/protoc/Parameter.java +++ b/plugin/src/main/java/com/flit/protoc/Parameter.java @@ -1,13 +1,12 @@ package com.flit.protoc; import com.flit.protoc.gen.GeneratorException; -import lombok.Getter; -import lombok.ToString; - import java.util.Arrays; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import lombok.Getter; +import lombok.ToString; @Getter @ToString public class Parameter { @@ -15,9 +14,10 @@ public static final String PARAM_CLIENT = "client"; public static final String PARAM_TYPE = "type"; public static final String PARAM_CONTEXT = "context"; + public static final String PARAM_REQUEST = "request"; - private String key; - private String value; + private final String key; + private final String value; public Parameter(String[] strings) { this.key = strings[0]; diff --git a/plugin/src/main/java/com/flit/protoc/Plugin.java b/plugin/src/main/java/com/flit/protoc/Plugin.java index 9a2986b..c64193e 100644 --- a/plugin/src/main/java/com/flit/protoc/Plugin.java +++ b/plugin/src/main/java/com/flit/protoc/Plugin.java @@ -1,5 +1,9 @@ package com.flit.protoc; +import static com.flit.protoc.Parameter.PARAM_REQUEST; +import static com.flit.protoc.Parameter.PARAM_TARGET; +import static com.flit.protoc.Parameter.PARAM_TYPE; + import com.flit.protoc.gen.Generator; import com.flit.protoc.gen.GeneratorException; import com.flit.protoc.gen.server.jaxrs.JaxrsGenerator; @@ -7,12 +11,11 @@ import com.flit.protoc.gen.server.undertow.UndertowGenerator; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse; - +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; -import static com.flit.protoc.Parameter.PARAM_TARGET; -import static com.flit.protoc.Parameter.PARAM_TYPE; - public class Plugin { private final CodeGeneratorRequest request; @@ -23,7 +26,9 @@ public Plugin(CodeGeneratorRequest request) { public CodeGeneratorResponse process() { if (!request.hasParameter()) { - return CodeGeneratorResponse.newBuilder().setError("Usage: --flit_out=target=server,type=[spring|undertow|jaxrs]:").build(); + return CodeGeneratorResponse.newBuilder() + .setError("Usage: --flit_out=target=server,type=[spring|undertow|jaxrs][,request=[class(es)]]:") + .build(); } Map params = Parameter.of(request.getParameter()); @@ -43,16 +48,17 @@ private Generator resolveGenerator(Map params) { if (!params.containsKey(PARAM_TYPE)) { throw new GeneratorException("No argument specified for type"); } + List requestServices = getRequestServices(params); switch (params.get(PARAM_TARGET).getValue()) { case "server": switch (params.get(PARAM_TYPE).getValue()) { case "boot": case "spring": - return new SpringGenerator(); + return new SpringGenerator(requestServices); case "undertow": - return new UndertowGenerator(); + return new UndertowGenerator(requestServices); case "jaxrs": - return new JaxrsGenerator(); + return new JaxrsGenerator(requestServices); default: throw new GeneratorException("Unknown server type: " + params.get(PARAM_TYPE).getValue()); } @@ -60,4 +66,13 @@ private Generator resolveGenerator(Map params) { throw new GeneratorException("Unknown target type: " + params.get(PARAM_TARGET).getValue()); } } + + private List getRequestServices(Map params) { + Parameter requestServices = params.get(PARAM_REQUEST); + if (requestServices == null) { + return Collections.emptyList(); + } else { + return Arrays.asList(requestServices.getValue().split(",")); + } + } } 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..4eed234 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,18 +1,18 @@ package com.flit.protoc.gen.server; +import static com.flit.protoc.Parameter.PARAM_CONTEXT; + import com.flit.protoc.Parameter; import com.flit.protoc.gen.Generator; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse; - +import com.squareup.javapoet.TypeName; import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.flit.protoc.Parameter.PARAM_CONTEXT; - /** * Implements the basic "generate a service interface + impl dispatcher". * @@ -21,13 +21,19 @@ */ public abstract class BaseServerGenerator implements Generator { + private final List requestServices; + + protected BaseServerGenerator(List requestServices) { + this.requestServices = requestServices; + } + @Override public List generate(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 ServiceGenerator(proto, s, mapper).getFiles()); + files.addAll(new ServiceGenerator(proto, s, mapper, isRequestBasedClass(s), getHttpRequestTypeName()).getFiles()); files.addAll(getRpcGenerator(proto, s, context, mapper).getFiles()); }); }); @@ -41,5 +47,11 @@ private static String getContext(Map params) { return null; } + protected boolean isRequestBasedClass(ServiceDescriptorProto service) { + return requestServices.contains(service.getName()); + } + protected abstract BaseGenerator getRpcGenerator(FileDescriptorProto proto, ServiceDescriptorProto service, String context, TypeMapper mapper); + + protected abstract TypeName getHttpRequestTypeName(); } 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..717bbc1 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 @@ -4,34 +4,48 @@ import com.google.protobuf.compiler.PluginProtos; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; - -import javax.lang.model.element.Modifier; import java.util.Collections; import java.util.List; +import javax.lang.model.element.Modifier; /** * Generates the `Rpc${SerivceName}` interface. * - * Currently this is the same interface across both undertow and spring. + * Currently, this is the same interface across both undertow and spring. */ public class ServiceGenerator extends BaseGenerator { private final TypeSpec.Builder rpcInterface; + private final boolean passRequest; + private final TypeName httpRequestType; - public ServiceGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s, TypeMapper mapper) { + public ServiceGenerator( + DescriptorProtos.FileDescriptorProto proto, + DescriptorProtos.ServiceDescriptorProto s, + TypeMapper mapper, + boolean passRequest, + TypeName httpRequestType + ) { super(proto, s, mapper); + this.passRequest = passRequest; + this.httpRequestType = httpRequestType; rpcInterface = TypeSpec.interfaceBuilder(ClassName.get(javaPackage, "Rpc" + service.getName())); rpcInterface.addModifiers(Modifier.PUBLIC); service.getMethodList().forEach(this::addHandleMethod); } private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { - rpcInterface.addMethod(MethodSpec.methodBuilder("handle" + m.getName()) + MethodSpec.Builder builder = MethodSpec.methodBuilder("handle" + m.getName()); + if (passRequest) { + builder.addParameter(httpRequestType, "request"); + } + builder .addParameter(mapper.get(m.getInputType()), "in") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) - .returns(mapper.get(m.getOutputType())) - .build()); + .returns(mapper.get(m.getOutputType())); + rpcInterface.addMethod(builder.build()); } @Override public List getFiles() { diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java index b538d2c..c4e0332 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/JaxrsGenerator.java @@ -1,16 +1,29 @@ package com.flit.protoc.gen.server.jaxrs; +import static com.flit.protoc.gen.server.jaxrs.RpcGenerator.HttpServletRequest; + import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.BaseServerGenerator; import com.flit.protoc.gen.server.TypeMapper; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; +import com.squareup.javapoet.TypeName; +import java.util.List; public class JaxrsGenerator extends BaseServerGenerator { + public JaxrsGenerator(List requestServices) { + super(requestServices); + } + @Override protected BaseGenerator getRpcGenerator(FileDescriptorProto proto, ServiceDescriptorProto service, String context, TypeMapper mapper) { - return new RpcGenerator(proto, service, context, mapper); + return new RpcGenerator(proto, service, context, mapper, isRequestBasedClass(service)); + } + + @Override + protected TypeName getHttpRequestTypeName() { + return HttpServletRequest; } } diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index dd75ded..d4819d6 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -29,11 +29,18 @@ public class RpcGenerator extends BaseGenerator { public static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); private final String context; private final Builder rpcResource; + private final boolean passRequest; - RpcGenerator(DescriptorProtos.FileDescriptorProto proto, - DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) { + RpcGenerator( + DescriptorProtos.FileDescriptorProto proto, + DescriptorProtos.ServiceDescriptorProto service, + String context, + TypeMapper mapper, + boolean passRequest + ) { super(proto, service, mapper); this.context = getContext(context); + this.passRequest = passRequest; this.rpcResource = TypeSpec.classBuilder(getResourceName(service)) .addModifiers(Modifier.PUBLIC) .addAnnotation( @@ -84,7 +91,7 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .addStatement("return") .endControlFlow() // route to the service - .addStatement("$T retval = service.handle$L(data)", outputType, mdp.getName()) + .addStatement(getRouteToService(), outputType, mdp.getName()) .addStatement("response.setStatus(200)") // send the response .beginControlFlow("if (json)") @@ -100,6 +107,14 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .build()); } + private String getRouteToService() { + if (passRequest) { + return "$T retval = service.handle$L(request, data)"; + } else { + return "$T retval = service.handle$L(data)"; + } + } + private ClassName getResourceName(DescriptorProtos.ServiceDescriptorProto service) { return ClassName.get(javaPackage, "Rpc" + service.getName() + "Resource"); } 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 b73d07b..4668311 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 @@ -22,10 +22,18 @@ class RpcGenerator extends BaseGenerator { private final String context; private final TypeSpec.Builder rpcController; + private final boolean passRequest; - RpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) { + RpcGenerator( + DescriptorProtos.FileDescriptorProto proto, + DescriptorProtos.ServiceDescriptorProto service, + String context, + TypeMapper mapper, + boolean passRequest + ) { super(proto, service, mapper); this.context = getContext(context); + this.passRequest = passRequest; rpcController = TypeSpec.classBuilder(getControllerName()).addModifiers(Modifier.PUBLIC).addAnnotation(RestController); addInstanceFields(); service.getMethodList().forEach(this::addHandleMethod); @@ -63,7 +71,7 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addStatement("return") .endControlFlow() // route to the service - .addStatement("$T retval = service.handle$L(data)", outputType, m.getName()) + .addStatement(getRouteToService(), outputType, m.getName()) .addStatement("response.setStatus(200)") // send the response .beginControlFlow("if (json)") @@ -76,7 +84,14 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addStatement("retval.writeTo(response.getOutputStream())") .endControlFlow() .build()); + } + private String getRouteToService() { + if (passRequest) { + return "$T retval = service.handle$L(request, data)"; + } else { + return "$T retval = service.handle$L(data)"; + } } private ClassName getControllerName() { 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..3a8ce18 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,19 +1,32 @@ package com.flit.protoc.gen.server.spring; +import static com.flit.protoc.gen.server.spring.RpcGenerator.HttpServletRequest; + import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.BaseServerGenerator; import com.flit.protoc.gen.server.TypeMapper; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; +import com.squareup.javapoet.TypeName; +import java.util.List; /** * Spring specific generator that will output MVC style routes. */ public class SpringGenerator extends BaseServerGenerator { - @Override protected BaseGenerator getRpcGenerator( + public SpringGenerator(List requestServices) { + super(requestServices); + } + + @Override + protected BaseGenerator getRpcGenerator( FileDescriptorProto proto, ServiceDescriptorProto service, String context, TypeMapper mapper) { - return new RpcGenerator(proto, service, context, mapper); + return new RpcGenerator(proto, service, context, mapper, isRequestBasedClass(service)); } + @Override + protected TypeName getHttpRequestTypeName() { + return HttpServletRequest; + } } 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 4fb4951..5ec23ba 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,31 +1,52 @@ package com.flit.protoc.gen.server.undertow; +import static com.flit.protoc.gen.server.Types.ErrorCode; +import static com.flit.protoc.gen.server.Types.ErrorWriter; +import static com.flit.protoc.gen.server.Types.Exception; +import static com.flit.protoc.gen.server.Types.FlitException; +import static com.flit.protoc.gen.server.Types.FlitHandler; +import static com.flit.protoc.gen.server.Types.Headers; +import static com.flit.protoc.gen.server.Types.HttpServerExchange; +import static com.flit.protoc.gen.server.Types.InputStreamReader; +import static com.flit.protoc.gen.server.Types.JsonFormat; +import static com.flit.protoc.gen.server.Types.Logger; +import static com.flit.protoc.gen.server.Types.LoggerFactory; +import static com.flit.protoc.gen.server.Types.Override; +import static com.flit.protoc.gen.server.Types.StandardCharsets; +import static javax.lang.model.element.Modifier.FINAL; +import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.PUBLIC; +import static javax.lang.model.element.Modifier.STATIC; + import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.TypeMapper; import com.flit.protoc.gen.server.Types; import com.google.common.net.MediaType; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.compiler.PluginProtos; -import com.squareup.javapoet.*; - -import java.lang.Override; -import java.lang.String; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; import java.util.Collections; import java.util.List; -import static com.flit.protoc.gen.server.Types.*; -import static com.flit.protoc.gen.server.Types.Exception; -import static com.flit.protoc.gen.server.Types.Override; -import static javax.lang.model.element.Modifier.*; - class RpcGenerator extends BaseGenerator { private final String context; private final TypeSpec.Builder rpcHandler; + private final boolean passRequest; - RpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) { + RpcGenerator( + DescriptorProtos.FileDescriptorProto proto, + DescriptorProtos.ServiceDescriptorProto service, + String context, TypeMapper mapper, + boolean passRequest + ) { super(proto, service, mapper); this.context = getContext(context); + this.passRequest = passRequest; rpcHandler = TypeSpec.classBuilder(getHandlerName(service)) .addModifiers(PUBLIC) .addSuperinterface(ClassName.bestGuess("io.undertow.server.HttpHandler")); @@ -123,7 +144,7 @@ private void writeHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .addStatement("return") .endControlFlow() // data is populated, now route to the service - .addStatement("$T response = service.handle$L(data)", outputType, m.getName()) + .addStatement(getRouteToService(), outputType, m.getName()) .addStatement("exchange.setStatusCode(200)") // put the result on the wire .beginControlFlow("if (json)") @@ -136,6 +157,14 @@ private void writeHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .build()); } + private String getRouteToService() { + if (passRequest) { + return "$T response = service.handle$L(exchange, data)"; + } else { + return "$T response = service.handle$L(data)"; + } + } + @Override public List getFiles() { return Collections.singletonList(toFile(getHandlerName(service), rpcHandler.build())); } 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..95f9323 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,16 +1,29 @@ package com.flit.protoc.gen.server.undertow; +import static com.flit.protoc.gen.server.Types.HttpServerExchange; + import com.flit.protoc.gen.server.BaseGenerator; import com.flit.protoc.gen.server.BaseServerGenerator; import com.flit.protoc.gen.server.TypeMapper; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; +import com.squareup.javapoet.TypeName; +import java.util.List; public class UndertowGenerator extends BaseServerGenerator { - @Override protected BaseGenerator getRpcGenerator( + public UndertowGenerator(List requestServices) { + super(requestServices); + } + + @Override + protected BaseGenerator getRpcGenerator( FileDescriptorProto proto, ServiceDescriptorProto service, String context, TypeMapper mapper) { - return new RpcGenerator(proto, service, context, mapper); + return new RpcGenerator(proto, service, context, mapper, isRequestBasedClass(service)); } + @Override + protected TypeName getHttpRequestTypeName() { + return HttpServerExchange; + } } diff --git a/plugin/src/test/java/com/flit/protoc/PluginTest.java b/plugin/src/test/java/com/flit/protoc/PluginTest.java index 1f627e8..2d0454d 100644 --- a/plugin/src/test/java/com/flit/protoc/PluginTest.java +++ b/plugin/src/test/java/com/flit/protoc/PluginTest.java @@ -1,12 +1,12 @@ package com.flit.protoc; -import com.google.protobuf.compiler.PluginProtos; -import org.junit.Test; - import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import com.google.protobuf.compiler.PluginProtos; +import org.junit.Test; + public class PluginTest { @Test public void test_NoParameters() { @@ -14,7 +14,7 @@ public class PluginTest { PluginProtos.CodeGeneratorResponse response = plugin.process(); assertTrue("Expected an error for no parameters", response.hasError()); - assertEquals("Incorrect error message", "Usage: --flit_out=target=server,type=[spring|undertow|jaxrs]:", response.getError()); + assertEquals("Incorrect error message", "Usage: --flit_out=target=server,type=[spring|undertow|jaxrs][,request=[class(es)]]:", response.getError()); } @Test public void test_NoTargetSpecified() { diff --git a/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java index 9bddea4..3ab9944 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java @@ -1,23 +1,29 @@ package com.flit.protoc.gen; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.github.javaparser.JavaParser; import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.compiler.PluginProtos; import com.google.protobuf.util.JsonFormat; -import org.apache.commons.io.FileUtils; - import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; - -import static java.nio.charset.StandardCharsets.UTF_8; +import org.apache.commons.io.FileUtils; public abstract class BaseGeneratorTest { public static PluginProtos.CodeGeneratorRequest loadJson(String resource) throws Exception { + return loadJson(resource, null); + } + + public static PluginProtos.CodeGeneratorRequest loadJson(String resource, String parameterOverride) throws Exception { try (InputStream is = BaseGeneratorTest.class.getClassLoader().getResource(resource).openStream()) { PluginProtos.CodeGeneratorRequest.Builder b = PluginProtos.CodeGeneratorRequest.newBuilder(); JsonFormat.parser().merge(new InputStreamReader(is), b); + if (parameterOverride != null) { + b.setParameter(parameterOverride); + } return b.build(); } } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java index fa0a5d5..695cf29 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java @@ -28,4 +28,20 @@ public void test_Generate() throws Exception { Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); response.getFileList().forEach(BaseGeneratorTest::assertParses); } + + @Test + public void test_GenerateWithRequest() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.jaxrs.json", "target=server,type=jaxrs,request=HelloWorld"); + + 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/RpcHelloWorldResource.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); + response.getFileList().forEach(BaseGeneratorTest::assertParses); + } } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt new file mode 100644 index 0000000..4dbc450 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -0,0 +1,91 @@ +[0] = package com.example.helloworld; + +import javax.servlet.http.HttpServletRequest; + +public interface RpcHelloWorld { + Helloworld.HelloResp handleHello(HttpServletRequest request, Helloworld.HelloReq in); + + Helloworld.HelloResp handleHelloAgain(HttpServletRequest request, Helloworld.HelloReq in); +} + +[1] = package com.example.helloworld; + +import com.google.protobuf.util.JsonFormat; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; + +@Path("/twirp/com.example.helloworld.HelloWorld") +public class RpcHelloWorldResource { + private final RpcHelloWorld service; + + public RpcHelloWorldResource(RpcHelloWorld service) { + this.service = service; + } + + @POST + @Path("/Hello") + public void handleHello(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHello(request, data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } + + @POST + @Path("/HelloAgain") + public void handleHelloAgain(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHelloAgain(request, data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } +} + diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java index 1ad9a37..ce9d3bb 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java @@ -1,15 +1,15 @@ package com.flit.protoc.gen.server.spring; +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + 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 { @@ -27,4 +27,19 @@ public class HelloworldGeneratorTest extends BaseGeneratorTest { response.getFileList().forEach(f -> assertParses(f)); } + @Test public void test_GenerateWithRequest() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.spring.json", "target=server,type=spring,request=HelloWorld"); + + 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/RpcHelloWorldController.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/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt new file mode 100644 index 0000000..8922a4c --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -0,0 +1,82 @@ +[0] = package com.example.helloworld; + +import javax.servlet.http.HttpServletRequest; + +public interface RpcHelloWorld { + Helloworld.HelloResp handleHello(HttpServletRequest request, Helloworld.HelloReq in); + + Helloworld.HelloResp handleHelloAgain(HttpServletRequest request, Helloworld.HelloReq in); +} + +[1] = package com.example.helloworld; + +import com.google.protobuf.util.JsonFormat; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class RpcHelloWorldController { + @Autowired + private RpcHelloWorld service; + + @PostMapping("/twirp/com.example.helloworld.HelloWorld/Hello") + public void handleHello(HttpServletRequest request, HttpServletResponse response) throws + Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + return; + } + Helloworld.HelloResp retval = service.handleHello(request, data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + } + + @PostMapping("/twirp/com.example.helloworld.HelloWorld/HelloAgain") + public void handleHelloAgain(HttpServletRequest request, HttpServletResponse response) throws + Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + return; + } + Helloworld.HelloResp retval = service.handleHelloAgain(request, data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + } +} + diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java index d45c36c..6982bc5 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java @@ -1,15 +1,15 @@ package com.flit.protoc.gen.server.undertow; +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + 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 { @@ -27,4 +27,19 @@ public class HelloworldGeneratorTest extends BaseGeneratorTest { response.getFileList().forEach(f -> assertParses(f)); } + @Test public void test_GenerateWithRequest() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.undertow.json", "target=server,type=undertow,request=HelloWorld"); + + 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/RpcHelloWorldHandler.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/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt new file mode 100644 index 0000000..ec2a09e --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -0,0 +1,117 @@ +[0] = package com.example.helloworld; + +import io.undertow.server.HttpServerExchange; + +public interface RpcHelloWorld { + Helloworld.HelloResp handleHello(HttpServerExchange request, Helloworld.HelloReq in); + + Helloworld.HelloResp handleHelloAgain(HttpServerExchange request, Helloworld.HelloReq in); +} + +[1] = package com.example.helloworld; + +import com.flit.runtime.ErrorCode; +import com.flit.runtime.FlitException; +import com.flit.runtime.undertow.ErrorWriter; +import com.flit.runtime.undertow.FlitHandler; +import com.google.protobuf.util.JsonFormat; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.Headers; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.lang.Override; +import java.lang.String; +import java.nio.charset.StandardCharsets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RpcHelloWorldHandler implements HttpHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RpcHelloWorldHandler.class); + + public static final String ROUTE = "/twirp/com.example.helloworld.HelloWorld"; + + private final RpcHelloWorld service; + + private final ErrorWriter errorWriter; + + public RpcHelloWorldHandler(RpcHelloWorld service) { + this.service = service; + this.errorWriter = new ErrorWriter(); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } + exchange.startBlocking(); + String method = exchange.getAttachment(FlitHandler.KEY_METHOD); + try { + switch (method) { + case "Hello": handleHello(exchange); break; + case "HelloAgain": handleHelloAgain(exchange); break; + default: throw FlitException.builder().withErrorCode(ErrorCode.BAD_ROUTE).withMessage("No such route").build(); + } + } catch (FlitException e) { + errorWriter.write(e, exchange); + } catch (Exception e) { + LOGGER.error("Exception caught at handler: {}", e.getMessage(), e); + errorWriter.write(e, exchange); + } + } + + private void handleHello(HttpServerExchange exchange) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + final String contentType = exchange.getRequestHeaders().get(Headers.CONTENT_TYPE).getFirst(); + if (contentType.equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(exchange.getInputStream()); + } else if (contentType.startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + exchange.setStatusCode(415); + return; + } + Helloworld.HelloResp response = service.handleHello(exchange, data); + exchange.setStatusCode(200); + if (json) { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json; charset=utf-8"); + exchange.getResponseSender().send(JsonFormat.printer().omittingInsignificantWhitespace().print(response)); + } else { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/protobuf"); + response.writeTo(exchange.getOutputStream()); + } + } + + private void handleHelloAgain(HttpServerExchange exchange) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + final String contentType = exchange.getRequestHeaders().get(Headers.CONTENT_TYPE).getFirst(); + if (contentType.equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(exchange.getInputStream()); + } else if (contentType.startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + exchange.setStatusCode(415); + return; + } + Helloworld.HelloResp response = service.handleHelloAgain(exchange, data); + exchange.setStatusCode(200); + if (json) { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json; charset=utf-8"); + exchange.getResponseSender().send(JsonFormat.printer().omittingInsignificantWhitespace().print(response)); + } else { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/protobuf"); + response.writeTo(exchange.getOutputStream()); + } + } +} + diff --git a/runtime/undertow/build.gradle b/runtime/undertow/build.gradle index 7f96269..3f6fa3f 100644 --- a/runtime/undertow/build.gradle +++ b/runtime/undertow/build.gradle @@ -33,7 +33,11 @@ dependencies { protobuf { protoc { - artifact = "com.google.protobuf:protoc:${protobufVersion}" + if (project.hasProperty('protoc_platform')) { + artifact = "com.google.protobuf:protoc:$protobufVersion:${protoc_platform}" + } else { + artifact = "com.google.protobuf:protoc:$protobufVersion" + } } plugins { flit { From 5c213cfce68b584185603389f720229f18cf6e05 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Wed, 14 Dec 2022 16:10:44 -0800 Subject: [PATCH 20/44] Fix bug with parsing multiple request parameters (#9) --- plugin/gradle.properties | 2 +- plugin/src/main/java/com/flit/protoc/Plugin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/gradle.properties b/plugin/gradle.properties index 2d48fba..2da9394 100644 --- a/plugin/gradle.properties +++ b/plugin/gradle.properties @@ -1 +1 @@ -version=1.2.1 +version=1.2.2 diff --git a/plugin/src/main/java/com/flit/protoc/Plugin.java b/plugin/src/main/java/com/flit/protoc/Plugin.java index c64193e..d67b519 100644 --- a/plugin/src/main/java/com/flit/protoc/Plugin.java +++ b/plugin/src/main/java/com/flit/protoc/Plugin.java @@ -72,7 +72,7 @@ private List getRequestServices(Map params) { if (requestServices == null) { return Collections.emptyList(); } else { - return Arrays.asList(requestServices.getValue().split(",")); + return Arrays.asList(requestServices.getValue().split(";")); } } } From 8bd9542be1ba9b846a2a361d6df32ce6f0e83dbc Mon Sep 17 00:00:00 2001 From: Andrei Anissimov Date: Thu, 15 Dec 2022 02:12:54 +0200 Subject: [PATCH 21/44] Fix gradle warnings (#8) Bump versions: gradle wrapper 4.8 -> 4.10, protobuf-java 3.5.1 -> 3.15.8, gradle shadow plugin 2.0.3 -> 4.0.3 --- gradle/wrapper/gradle-wrapper.properties | 2 +- plugin/build.gradle | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 58c411e..317cc1f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip diff --git a/plugin/build.gradle b/plugin/build.gradle index db5e390..1fb3447 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.3' + classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.3' } } @@ -37,11 +37,12 @@ dependencies { compile 'org.slf4j:slf4j-api:1.7.25' compile 'ch.qos.logback:logback-classic:1.2.3' compile 'commons-io:commons-io:2.5' - compile 'com.google.protobuf:protobuf-java:3.5.1' - compile 'com.google.protobuf:protobuf-java-util:3.5.1' + compile 'com.google.protobuf:protobuf-java:3.15.8' + compile 'com.google.protobuf:protobuf-java-util:3.15.8' compile 'com.squareup:javapoet:1.11.1' compileOnly('org.projectlombok:lombok:+') + annotationProcessor 'org.projectlombok:lombok:+' testCompile 'com.github.javaparser:javaparser-core:3.6.9' testCompile 'junit:junit:4.12' From 373a4f2a27163925f17903e952ce45cef8e1cbbf Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Thu, 6 Apr 2023 11:14:16 -0700 Subject: [PATCH 22/44] update all the things gradle,libraries,tests,version (#10) --- .github/workflows/build.yaml | 31 ++++ .github/workflows/codeql-analysis.yml | 74 ++++++++ .github/workflows/gradle.yml | 26 --- .github/workflows/publish.yaml | 26 +++ README.md | 13 +- build.gradle | 90 ++++++---- gradle/wrapper/gradle-wrapper.jar | Bin 54413 -> 56172 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew.bat | 168 +++++++++--------- plugin/build.gradle | 74 ++------ plugin/gradle.properties | 1 - .../main/java/com/flit/protoc/Parameter.java | 20 ++- .../test/java/com/flit/protoc/PluginTest.java | 90 ++++++---- .../flit/protoc/gen/BaseGeneratorTest.java | 14 +- .../protoc/gen/server/TypeMapperTest.java | 13 +- .../server/jaxrs/ContextGeneratorTest.java | 8 +- .../server/jaxrs/HelloworldGeneratorTest.java | 6 +- .../gen/server/jaxrs/StatusGeneratorTest.java | 9 +- .../server/spring/ContextGeneratorTest.java | 24 ++- .../spring/HelloworldGeneratorTest.java | 12 +- .../server/spring/StatusGeneratorTest.java | 13 +- .../server/undertow/ContextGeneratorTest.java | 24 ++- .../undertow/HelloworldGeneratorTest.java | 12 +- .../server/undertow/StatusGeneratorTest.java | 13 +- runtime/core/build.gradle | 26 ++- runtime/core/gradle.properties | 1 - runtime/jaxrs/build.gradle | 33 ++-- runtime/jaxrs/gradle.properties | 1 - .../jaxrs/FlitExceptionMapperTest.java | 14 +- .../InvalidProtobufExceptionMapperTest.java | 14 +- runtime/spring/build.gradle | 30 ++-- runtime/spring/gradle.properties | 1 - runtime/undertow/build.gradle | 68 ++----- runtime/undertow/gradle.properties | 1 - 34 files changed, 505 insertions(+), 448 deletions(-) create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/codeql-analysis.yml delete mode 100644 .github/workflows/gradle.yml create mode 100644 .github/workflows/publish.yaml delete mode 100644 plugin/gradle.properties delete mode 100644 runtime/core/gradle.properties delete mode 100644 runtime/jaxrs/gradle.properties delete mode 100644 runtime/spring/gradle.properties delete mode 100644 runtime/undertow/gradle.properties diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..07dcc48 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'zulu' + - name: Build with Gradle + uses: gradle/gradle-build-action@v2 + - name: Build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} + run: ./gradlew build diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..c0546fd --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '40 1 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + packages: read + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml deleted file mode 100644 index d131978..0000000 --- a/.github/workflows/gradle.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This workflow will build a Java project with Gradle -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle - -name: Java CI with Gradle - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.11 - uses: actions/setup-java@v1 - with: - java-version: 1.11 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew clean build diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..ece8ffa --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,26 @@ +name: Publish to GitHub Packages + +on: + push: + branches: + - main + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'zulu' + - name: Publish with Gradle + uses: gradle/gradle-build-action@v2 + - name: Publish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} + run: ./gradlew publish diff --git a/README.md b/README.md index 5d938e1..8c310d5 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,12 @@ It supports the generation of Java based servers with the following flavours sup ### Requirements -The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/zulu-community/?architecture=x86-64-bit&package=jdk "JDK Downloads") (version 11) +The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/#zulu "JDK Downloads") version 11. The build uses gradle to generate the artifacts. No installation is required as the project uses the [gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html "gradle wrapper") setup. -For testing you will need an installation of the [protocol buffers compiler](https://github.com/google/protobuf/releases "protobuf releases"). +To test you will need an installation of the [protocol buffers compiler](https://github.com/protocolbuffers/protobuf/releases "protobuf releases"). ### Modules @@ -37,9 +37,9 @@ The project is split into the following modules: To build the various components, run the following: - git clone git@github.com:devork/flit.git + git clone git@github.com:github/flit.git cd flit - ./gradlew clean build pack + ./gradlew clean build ### Installation @@ -47,10 +47,7 @@ Currently, the run script only supports *nix but the run script should be fairly After building: - cp plugin/build/package/flit-plugin.zip /usr/local/bin - cd /usr/local/bin - unzip flit-plugin.zip - chmod +x protoc-gen-flit + cp plugin/build/libs/plugin-all.jar /code/java-project/scripts/ ## Running diff --git a/build.gradle b/build.gradle index 9e30e8e..3d123b7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,38 +1,64 @@ -buildscript { +plugins { + id 'java-library' + id 'maven-publish' + id 'dev.poolside.gradle.semantic-version' version '0.1.5' +} + +allprojects { + apply plugin: 'java-library' + apply plugin: 'maven-publish' + repositories { - jcenter() + mavenCentral() + } + + ext { + // third party + javapoetVersion = "1.13.0" + javaxservletapiVersion = "4.0.1" + javaxwsrsapiVersion = "2.1.1" + protobufVersion = "3.22.2" + slf4jVersion = "1.7.36" + springVersion = "5.3.26" + undertowVersion = "2.3.5.Final" + + // testing + approvaltestsVersion = "18.5.0" + javaparserVersion = "3.25.2" + jerseyCommonVersion = "2.22.2" + junitJupiterVersion = "5.9.2" + mockitoVersion = "5.2.0" + } + + java { + group = "com.flit" + targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 + withSourcesJar() + } + + test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } } + dependencies { - classpath('org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r') + testImplementation(platform("org.junit:junit-bom:$junitJupiterVersion")) + testImplementation('org.junit.jupiter:junit-jupiter') } -} -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.storage.file.FileRepositoryBuilder - -import java.time.Instant -import java.time.ZoneOffset -import java.time.format.DateTimeFormatter - -static def scmInfo(String path) { - FileRepositoryBuilder builder = new FileRepositoryBuilder() - Repository repository = builder.setGitDir(new File("${path}/.git")) - .readEnvironment() - .findGitDir() - .build() - - def branch = repository.getBranch() - def head = repository.resolve("HEAD") - def revision = head == null ? "UNKNOWN" : head.name() - def date = Instant.now().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - def tstamp = Instant.now().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) - - return [ - branch : branch, - revision: revision, - date : date, - tstamp : tstamp - ] + publishing { + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/github/flit") + credentials { + username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN") + } + } + } + } } - -project.ext.scm = scmInfo(rootDir.getAbsolutePath()) \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1948b9074f1016d15d505d185bc3f73deb82d8c8..28861d273a5d270fd8f65dd74570c17c9c507736 100644 GIT binary patch delta 49416 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%0A>9N>1DoF?DLG9Cx#t>vp)Y=DBpcbo3xSPiI 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 delta 47706 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 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/plugin/build.gradle b/plugin/build.gradle index 1fb3447..6371af6 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -1,72 +1,30 @@ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.3' - } +plugins { + id "com.github.johnrengelman.shadow" version "8.1.1" } -project.ext.scm = scmInfo(rootDir.getAbsolutePath()) - -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'java' - -group 'com.flit' - -sourceCompatibility = 1.8 +dependencies { + implementation("com.google.protobuf:protobuf-java:$protobufVersion") + implementation("com.google.protobuf:protobuf-java-util:$protobufVersion") + implementation("com.squareup:javapoet:$javapoetVersion") -configurations { - zip + testImplementation("com.github.javaparser:javaparser-core:$javaparserVersion") + testImplementation("com.approvaltests:approvaltests:$approvaltestsVersion") } shadowJar { manifest { attributes( - 'Main-Class': "com.flit.protoc.Main" + 'Main-Class': "com.flit.protoc.Main" ) } } -repositories { - mavenCentral() -} - -dependencies { - compile 'com.google.guava:guava:25.1-jre' - compile 'org.slf4j:slf4j-api:1.7.25' - compile 'ch.qos.logback:logback-classic:1.2.3' - compile 'commons-io:commons-io:2.5' - compile 'com.google.protobuf:protobuf-java:3.15.8' - compile 'com.google.protobuf:protobuf-java-util:3.15.8' - compile 'com.squareup:javapoet:1.11.1' - - compileOnly('org.projectlombok:lombok:+') - annotationProcessor 'org.projectlombok:lombok:+' - - testCompile 'com.github.javaparser:javaparser-core:3.6.9' - testCompile 'junit:junit:4.12' - testCompile 'com.approvaltests:approvaltests:2.0.0' -} - -shadowJar.dependsOn compileJava - -task pack(type: Zip) { - archiveName = "flit-plugin.zip" - destinationDir = file("${buildDir}/package") - - from("${buildDir}/libs") { - include "plugin-*-all.jar" - } - - from("${projectDir}/src/main/shell") { - include("*") +publishing { + publications { + shadow(MavenPublication) { publication -> + artifactId = 'flit-plugin' + version = '1.3' + project.shadow.component(publication) + } } } - -pack.dependsOn shadowJar - -artifacts { - zip pack -} - diff --git a/plugin/gradle.properties b/plugin/gradle.properties deleted file mode 100644 index 2da9394..0000000 --- a/plugin/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -version=1.2.2 diff --git a/plugin/src/main/java/com/flit/protoc/Parameter.java b/plugin/src/main/java/com/flit/protoc/Parameter.java index aa70b7c..8131226 100644 --- a/plugin/src/main/java/com/flit/protoc/Parameter.java +++ b/plugin/src/main/java/com/flit/protoc/Parameter.java @@ -5,10 +5,8 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import lombok.Getter; -import lombok.ToString; -@Getter @ToString public class Parameter { +public class Parameter { public static final String PARAM_TARGET = "target"; public static final String PARAM_CLIENT = "client"; @@ -24,6 +22,22 @@ public Parameter(String[] strings) { this.value = strings[1]; } + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return "Parameter{" + + "key='" + key + '\'' + + ", value='" + value + '\'' + + '}'; + } + public static Map of(String value) { if (value == null || (value = value.trim()).isEmpty()) { throw new GeneratorException("Empty value passed to parameter builder"); diff --git a/plugin/src/test/java/com/flit/protoc/PluginTest.java b/plugin/src/test/java/com/flit/protoc/PluginTest.java index 2d0454d..0109fd7 100644 --- a/plugin/src/test/java/com/flit/protoc/PluginTest.java +++ b/plugin/src/test/java/com/flit/protoc/PluginTest.java @@ -1,76 +1,96 @@ package com.flit.protoc; -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.protobuf.compiler.PluginProtos; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class PluginTest { - @Test public void test_NoParameters() { + @Test + public void test_NoParameters() { Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for no parameters", response.hasError()); - assertEquals("Incorrect error message", "Usage: --flit_out=target=server,type=[spring|undertow|jaxrs][,request=[class(es)]]:", response.getError()); + assertTrue(response.hasError(), "Expected an error for no parameters"); + assertEquals( + "Usage: --flit_out=target=server,type=[spring|undertow|jaxrs][,request=[class(es)]]:", + response.getError()); } - @Test public void test_NoTargetSpecified() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("unknown=unknown").build()); + @Test + public void test_NoTargetSpecified() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("unknown=unknown").build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for unknown target type", response.hasError()); - assertEquals("Incorrect error message", "No argument specified for target", response.getError()); + assertTrue(response.hasError(), "Expected an error for unknown target type"); + assertEquals("No argument specified for target", response.getError()); } - @Test public void test_UnknownTargetType() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=unknown,type=boot").build()); + @Test + public void test_UnknownTargetType() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=unknown,type=boot") + .build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for unknown target type", response.hasError()); - assertEquals("Incorrect error message", "Unknown target type: unknown", response.getError()); + assertTrue(response.hasError(), "Expected an error for unknown target type"); + assertEquals("Unknown target type: unknown", response.getError()); } - @Test public void test_EmptyTargetType() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=").build()); + @Test + public void test_EmptyTargetType() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=").build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for unknown target type", response.hasError()); - assertEquals("Incorrect error message", "No argument specified for target", response.getError()); + assertTrue(response.hasError(), "Expected an error for unknown target type"); + assertEquals("No argument specified for target", response.getError()); } - @Test public void test_MissingTargetType() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server").build()); + @Test + public void test_MissingTargetType() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server").build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for unknown server type", response.hasError()); - assertEquals("Incorrect error message", "No argument specified for type", response.getError()); + assertTrue(response.hasError(), "Expected an error for unknown server type"); + assertEquals("No argument specified for type", response.getError()); } - @Test public void test_UnknownServerType() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server,type=unknown").build()); + @Test + public void test_UnknownServerType() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server,type=unknown") + .build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for unknown server type", response.hasError()); - assertEquals("Incorrect error message", "Unknown server type: unknown", response.getError()); + assertTrue(response.hasError(), "Expected an error for unknown server type"); + assertEquals("Unknown server type: unknown", response.getError()); } - @Test public void test_MissingServerType() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server,type=").build()); + @Test + public void test_MissingServerType() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server,type=").build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertTrue("Expected an error for unknown server type", response.hasError()); - assertEquals("Incorrect error message", "No argument specified for type", response.getError()); + assertTrue(response.hasError(), "Expected an error for unknown server type"); + assertEquals("No argument specified for type", response.getError()); } - @Test public void test_EmptyProtoList() { - Plugin plugin = new Plugin(PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server,type=boot").build()); + @Test + public void test_EmptyProtoList() { + Plugin plugin = new Plugin( + PluginProtos.CodeGeneratorRequest.newBuilder().setParameter("target=server,type=boot") + .build()); PluginProtos.CodeGeneratorResponse response = plugin.process(); - assertFalse("No error expected for empty file list", response.hasError()); - assertEquals("Expected no files generated", 0, response.getFileCount()); + assertFalse(response.hasError(), "No error expected for empty file list"); + assertEquals(0, response.getFileCount()); } } diff --git a/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java index 3ab9944..3dcc7b7 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/BaseGeneratorTest.java @@ -1,15 +1,10 @@ package com.flit.protoc.gen; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.github.javaparser.JavaParser; -import com.google.protobuf.MessageOrBuilder; +import com.github.javaparser.StaticJavaParser; import com.google.protobuf.compiler.PluginProtos; import com.google.protobuf.util.JsonFormat; -import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; -import org.apache.commons.io.FileUtils; public abstract class BaseGeneratorTest { @@ -28,14 +23,9 @@ public static PluginProtos.CodeGeneratorRequest loadJson(String resource, String } } - // For converting initial .bin dumps over to .json - public static void saveAsJson(MessageOrBuilder request, String fileName) throws Exception { - FileUtils.writeStringToFile(new File(fileName), JsonFormat.printer().print(request), UTF_8); - } - protected static void assertParses(PluginProtos.CodeGeneratorResponse.File file) { try { - JavaParser.parse(file.getContent()); + StaticJavaParser.parse(file.getContent()); } catch (Exception e) { throw new RuntimeException("Could not parse " + file.getName(), e); } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java index aedd91a..57e7bc9 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/TypeMapperTest.java @@ -1,11 +1,12 @@ package com.flit.protoc.gen.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.protobuf.DescriptorProtos; import com.squareup.javapoet.ClassName; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class TypeMapperTest { @@ -159,7 +160,7 @@ public void javaPackageWithOuterClass() { assertEquals("Map", result.simpleName()); } - @Test(expected = IllegalArgumentException.class) + @Test public void javaPackageWithOuterClassEmpty() { DescriptorProtos.FileOptions options = DescriptorProtos.FileOptions.newBuilder() .setJavaMultipleFiles(false) @@ -175,9 +176,7 @@ public void javaPackageWithOuterClassEmpty() { .build(); TypeMapper mapper = new TypeMapper(); - mapper.add(proto); - - mapper.get(".flit.test.Map"); + assertThrows(IllegalArgumentException.class, () -> mapper.add(proto)); } @Test diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java index 6de5f88..dab7a3b 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/ContextGeneratorTest.java @@ -1,8 +1,8 @@ package com.flit.protoc.gen.server.jaxrs; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.flit.protoc.Plugin; import com.flit.protoc.gen.BaseGeneratorTest; @@ -11,7 +11,7 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * Tests the generation of a service that has core definition imported from another file diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java index 695cf29..f5da202 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.java @@ -1,15 +1,15 @@ package com.flit.protoc.gen.server.jaxrs; import static java.util.stream.Collectors.toList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import com.flit.protoc.Plugin; import com.flit.protoc.gen.BaseGeneratorTest; import com.google.protobuf.compiler.PluginProtos; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; import org.approvaltests.Approvals; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class HelloworldGeneratorTest extends BaseGeneratorTest { diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java index 742db73..9b802b3 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.java @@ -1,22 +1,23 @@ package com.flit.protoc.gen.server.jaxrs; import static java.util.stream.Collectors.toList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import com.flit.protoc.Plugin; import com.flit.protoc.gen.BaseGeneratorTest; import com.google.protobuf.compiler.PluginProtos; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; import org.approvaltests.Approvals; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * 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 { + @Test + public void test_Generate() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("status.jaxrs.json"); Plugin plugin = new Plugin(request); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/ContextGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/spring/ContextGeneratorTest.java index b16a601..06fb89b 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/ContextGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/ContextGeneratorTest.java @@ -1,38 +1,44 @@ package com.flit.protoc.gen.server.spring; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.flit.protoc.Plugin; import com.flit.protoc.gen.BaseGeneratorTest; import com.google.protobuf.compiler.PluginProtos; -import org.junit.Test; - import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; - -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; /** * Tests the generation of a service that has core definition imported from another file */ public class ContextGeneratorTest extends BaseGeneratorTest { - @Test public void test_GenerateWithMissingRoot() throws Exception { + @Test + public void test_GenerateWithMissingRoot() throws Exception { test_Route("context.missing.spring.json", "/twirp/com.example.context.NullService/SayNull"); } - @Test public void test_GenerateWithEmptyRoot() throws Exception { + @Test + public void test_GenerateWithEmptyRoot() throws Exception { test_Route("context.empty.spring.json", "/twirp/com.example.context.NullService/SayNull"); } - @Test public void test_GenerateWithSlashOnlyRoot() throws Exception { + @Test + public void test_GenerateWithSlashOnlyRoot() throws Exception { test_Route("context.slash.spring.json", "/com.example.context.NullService/SayNull"); } - @Test public void test_GenerateWithSlashRoot() throws Exception { + @Test + public void test_GenerateWithSlashRoot() throws Exception { test_Route("context.root.spring.json", "/root/com.example.context.NullService/SayNull"); } - @Test public void test_GenerateWithNameRoot() throws Exception { + @Test + public void test_GenerateWithNameRoot() throws Exception { test_Route("context.name.spring.json", "/fibble/com.example.context.NullService/SayNull"); } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java index ce9d3bb..5fcdba2 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.java @@ -1,18 +1,19 @@ package com.flit.protoc.gen.server.spring; import static java.util.stream.Collectors.toList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; 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 org.junit.jupiter.api.Test; public class HelloworldGeneratorTest extends BaseGeneratorTest { - @Test public void test_Generate() throws Exception { + @Test + public void test_Generate() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.spring.json"); Plugin plugin = new Plugin(request); @@ -27,7 +28,8 @@ public class HelloworldGeneratorTest extends BaseGeneratorTest { response.getFileList().forEach(f -> assertParses(f)); } - @Test public void test_GenerateWithRequest() throws Exception { + @Test + public void test_GenerateWithRequest() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.spring.json", "target=server,type=spring,request=HelloWorld"); Plugin plugin = new Plugin(request); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.java index 690ec1f..924f122 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.java @@ -1,21 +1,22 @@ package com.flit.protoc.gen.server.spring; +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + 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; +import org.junit.jupiter.api.Test; /** * 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 { + @Test + public void test_Generate() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("status.spring.json"); Plugin plugin = new Plugin(request); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/ContextGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/ContextGeneratorTest.java index cb835d4..64b1bbc 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/ContextGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/ContextGeneratorTest.java @@ -1,38 +1,44 @@ package com.flit.protoc.gen.server.undertow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.flit.protoc.Plugin; import com.flit.protoc.gen.BaseGeneratorTest; import com.google.protobuf.compiler.PluginProtos; -import org.junit.Test; - import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; - -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; /** * Tests the generation of a service that has core definition imported from another file */ public class ContextGeneratorTest extends BaseGeneratorTest { - @Test public void test_GenerateWithMissingRoot() throws Exception { + @Test + public void test_GenerateWithMissingRoot() throws Exception { test_Route("context.missing.undertow.json", "/twirp/com.example.context.NullService"); } - @Test public void test_GenerateWithEmptyRoot() throws Exception { + @Test + public void test_GenerateWithEmptyRoot() throws Exception { test_Route("context.empty.undertow.json", "/twirp/com.example.context.NullService"); } - @Test public void test_GenerateWithSlashOnlyRoot() throws Exception { + @Test + public void test_GenerateWithSlashOnlyRoot() throws Exception { test_Route("context.slash.undertow.json", "/com.example.context.NullService"); } - @Test public void test_GenerateWithSlashRoot() throws Exception { + @Test + public void test_GenerateWithSlashRoot() throws Exception { test_Route("context.root.undertow.json", "/root/com.example.context.NullService"); } - @Test public void test_GenerateWithNameRoot() throws Exception { + @Test + public void test_GenerateWithNameRoot() throws Exception { test_Route("context.name.undertow.json", "/fibble/com.example.context.NullService"); } diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java index 6982bc5..e0eaee3 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.java @@ -1,18 +1,19 @@ package com.flit.protoc.gen.server.undertow; import static java.util.stream.Collectors.toList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; 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 org.junit.jupiter.api.Test; public class HelloworldGeneratorTest extends BaseGeneratorTest { - @Test public void test_Generate() throws Exception { + @Test + public void test_Generate() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.undertow.json"); Plugin plugin = new Plugin(request); @@ -27,7 +28,8 @@ public class HelloworldGeneratorTest extends BaseGeneratorTest { response.getFileList().forEach(f -> assertParses(f)); } - @Test public void test_GenerateWithRequest() throws Exception { + @Test + public void test_GenerateWithRequest() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.undertow.json", "target=server,type=undertow,request=HelloWorld"); Plugin plugin = new Plugin(request); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.java index 706ffc3..91ebad4 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.java +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.java @@ -1,21 +1,22 @@ package com.flit.protoc.gen.server.undertow; +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + 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; +import org.junit.jupiter.api.Test; /** * 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 { + @Test + public void test_Generate() throws Exception { PluginProtos.CodeGeneratorRequest request = loadJson("status.undertow.json"); Plugin plugin = new Plugin(request); diff --git a/runtime/core/build.gradle b/runtime/core/build.gradle index ee87f0f..06ad5ec 100644 --- a/runtime/core/build.gradle +++ b/runtime/core/build.gradle @@ -1,19 +1,15 @@ -plugins { - id 'java' -} - -group 'com.flit' -archivesBaseName = 'flit-core-runtime' -sourceCompatibility = 1.8 -def protobufVersion = '3.5.1' - -repositories { - mavenCentral() +dependencies { + api("com.google.protobuf:protobuf-java:${protobufVersion}") + api("com.google.protobuf:protobuf-java-util:${protobufVersion}") } -dependencies { - compile "com.google.protobuf:protobuf-java:${protobufVersion}" - compile "com.google.protobuf:protobuf-java-util:${protobufVersion}" +publishing { + publications { + maven(MavenPublication) { + artifactId = 'flit-core-runtime' + version = "1.3" - testCompile 'junit:junit:4.12' + from components.java + } + } } diff --git a/runtime/core/gradle.properties b/runtime/core/gradle.properties deleted file mode 100644 index 1bf5794..0000000 --- a/runtime/core/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -version=1.2.0 diff --git a/runtime/jaxrs/build.gradle b/runtime/jaxrs/build.gradle index 86213fe..089c4a9 100644 --- a/runtime/jaxrs/build.gradle +++ b/runtime/jaxrs/build.gradle @@ -1,21 +1,20 @@ -plugins { - id 'java' -} - -group 'com.flit' -archivesBaseName = 'flit-jaxrs-runtime' -sourceCompatibility = 1.8 +dependencies { + implementation(project(':runtime:core')) + implementation("javax.servlet:javax.servlet-api:$javaxservletapiVersion") + implementation("javax.ws.rs:javax.ws.rs-api:$javaxwsrsapiVersion") + implementation("org.slf4j:slf4j-api:$slf4jVersion") -repositories { - mavenCentral() + testImplementation("org.glassfish.jersey.core:jersey-common:$jerseyCommonVersion") + testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion") } -dependencies { - compile project(':runtime:core') - compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1' - compile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.1.1' - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.glassfish.jersey.core', name: 'jersey-common', version: '2.22.2' - testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' +publishing { + publications { + maven(MavenPublication) { + artifactId = 'flit-jaxrs-runtime' + version = "1.3" + + from components.java + } + } } diff --git a/runtime/jaxrs/gradle.properties b/runtime/jaxrs/gradle.properties deleted file mode 100644 index 1bf5794..0000000 --- a/runtime/jaxrs/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -version=1.2.0 diff --git a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java index 4cd440f..d09b89e 100644 --- a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java +++ b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/FlitExceptionMapperTest.java @@ -1,6 +1,6 @@ package com.flit.runtime.jaxrs; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.flit.runtime.ErrorCode; import com.flit.runtime.FlitException; @@ -9,12 +9,13 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) public class FlitExceptionMapperTest { @Mock @@ -23,11 +24,6 @@ public class FlitExceptionMapperTest { @InjectMocks private FlitExceptionMapper flitExceptionMapper = new FlitExceptionMapper(); - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - @Test public void testToResponse() { FlitException flit = FlitException.builder() diff --git a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java index 9b13952..aa9f5b4 100644 --- a/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java +++ b/runtime/jaxrs/src/test/java/com/flit/runtime/jaxrs/InvalidProtobufExceptionMapperTest.java @@ -1,18 +1,19 @@ package com.flit.runtime.jaxrs; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.protobuf.InvalidProtocolBufferException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) public class InvalidProtobufExceptionMapperTest { @Mock @@ -21,11 +22,6 @@ public class InvalidProtobufExceptionMapperTest { @InjectMocks private InvalidProtobufExceptionMapper mapper = new InvalidProtobufExceptionMapper(); - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - @Test public void testToResponse() { InvalidProtocolBufferException exception = new InvalidProtocolBufferException( diff --git a/runtime/spring/build.gradle b/runtime/spring/build.gradle index e86159b..6478bfe 100644 --- a/runtime/spring/build.gradle +++ b/runtime/spring/build.gradle @@ -1,21 +1,17 @@ -plugins { - id 'java' -} - -group 'com.flit' -archivesBaseName = 'flit-spring-runtime' -sourceCompatibility = 1.8 - -repositories { - mavenCentral() -} - dependencies { + compileOnly(project(':runtime:core')) + compileOnly("org.slf4j:slf4j-api:$slf4jVersion") + compileOnly("javax.servlet:javax.servlet-api:$javaxservletapiVersion") + compileOnly("org.springframework:spring-webmvc:$springVersion") +} - compileOnly project(':runtime:core') - compileOnly 'org.slf4j:slf4j-api:1.7.25' - compileOnly 'javax.servlet:javax.servlet-api:4.0.1' - compileOnly 'org.springframework:spring-webmvc:5.0.6.RELEASE' +publishing { + publications { + maven(MavenPublication) { + artifactId = 'flit-spring-runtime' + version = "1.3" - testCompile 'junit:junit:4.12' + from components.java + } + } } diff --git a/runtime/spring/gradle.properties b/runtime/spring/gradle.properties deleted file mode 100644 index 1bf5794..0000000 --- a/runtime/spring/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -version=1.2.0 diff --git a/runtime/undertow/build.gradle b/runtime/undertow/build.gradle index 3f6fa3f..ddcb74f 100644 --- a/runtime/undertow/build.gradle +++ b/runtime/undertow/build.gradle @@ -1,64 +1,16 @@ -plugins { - id 'java' - id 'idea' - id "com.google.protobuf" version "0.8.6" -} - -group 'com.flit' -archivesBaseName = 'flit-undertow-runtime' -sourceCompatibility = 1.8 - -repositories { - mavenCentral() -} - -// Make a flit configuration for testing our runtime -configurations { - testFlit -} - -def protobufVersion = '3.5.1' -def flitDir = "$buildDir/flit" - dependencies { - compile project(':runtime:core') - compile 'org.slf4j:slf4j-api:1.7.25' - compile 'io.undertow:undertow-core:2.0.9.Final' - compile 'com.google.code.gson:gson:2.8.5' - - testCompile 'junit:junit:4.12' - - testFlit project(path: ':plugin', configuration: 'zip') + implementation(project(':runtime:core')) + compileOnly("org.slf4j:slf4j-api:$slf4jVersion") + compileOnly("io.undertow:undertow-core:$undertowVersion") } -protobuf { - protoc { - if (project.hasProperty('protoc_platform')) { - artifact = "com.google.protobuf:protoc:$protobufVersion:${protoc_platform}" - } else { - artifact = "com.google.protobuf:protoc:$protobufVersion" - } - } - plugins { - flit { - path = "${flitDir}/protoc-gen-flit" - } - } - generateProtoTasks { - all().each { task -> - task.plugins { - flit { - option 'target=server' - option 'type=undertow' - } - } - task.dependsOn unzipFlit +publishing { + publications { + maven(MavenPublication) { + artifactId = 'flit-undertow-runtime' + version = "1.3" + + from components.java } } } - -task unzipFlit(type: Copy) { - inputs.files configurations.testFlit - from configurations.testFlit.findAll { it.name.endsWith('zip') }.collect { zipTree(it) } - into flitDir -} diff --git a/runtime/undertow/gradle.properties b/runtime/undertow/gradle.properties deleted file mode 100644 index 1bf5794..0000000 --- a/runtime/undertow/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -version=1.2.0 From ccd9b6b1dece55e53acf24f2fcc48b4977783a96 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Thu, 6 Apr 2023 11:16:06 -0700 Subject: [PATCH 23/44] rename branch from main to master --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/publish.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c0546fd..20cc662 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ main ] + branches: [ master ] pull_request: # The branches below must be a subset of the branches above - branches: [ main ] + branches: [ master ] schedule: - cron: '40 1 * * 2' diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index ece8ffa..21cc611 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -3,7 +3,7 @@ name: Publish to GitHub Packages on: push: branches: - - main + - master jobs: publish: From 440bfdd8695fc2aa6bb4a23ffde631ea07e150b8 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Tue, 2 May 2023 14:32:36 -0700 Subject: [PATCH 24/44] add jakarta implementation of plugin and runtime jars (#11) --- README.md | 28 ++-- build.gradle | 5 +- .../src/main/java/com/flit/protoc/Plugin.java | 3 + .../gen/server/jakarta/JakartaGenerator.java | 29 ++++ .../gen/server/jakarta/RpcGenerator.java | 129 ++++++++++++++++++ .../protoc/gen/server/jaxrs/RpcGenerator.java | 18 ++- .../gen/server/spring/RpcGenerator.java | 10 +- .../server/jakarta/ContextGeneratorTest.java | 67 +++++++++ .../jakarta/HelloworldGeneratorTest.java | 47 +++++++ ...ldGeneratorTest.test_Generate.approved.txt | 89 ++++++++++++ ...Test.test_GenerateWithRequest.approved.txt | 91 ++++++++++++ .../server/jakarta/StatusGeneratorTest.java | 36 +++++ ...usGeneratorTest.test_Generate.approved.txt | 57 ++++++++ .../test/resources/context.empty.jakarta.json | 29 ++++ .../resources/context.missing.jakarta.json | 29 ++++ .../test/resources/context.name.jakarta.json | 29 ++++ .../test/resources/context.root.jakarta.json | 29 ++++ .../test/resources/context.slash.jakarta.json | 29 ++++ .../test/resources/helloworld.jakarta.json | 49 +++++++ plugin/src/test/resources/status.jakarta.json | 79 +++++++++++ runtime/jakarta/build.gradle | 20 +++ .../runtime/jakarta/FlitExceptionMapper.java | 41 ++++++ .../InvalidProtobufExceptionMapper.java | 36 +++++ .../jakarta/FlitExceptionMapperTest.java | 40 ++++++ .../InvalidProtobufExceptionMapperTest.java | 36 +++++ runtime/jaxrs/build.gradle | 2 +- settings.gradle | 2 +- 27 files changed, 1028 insertions(+), 31 deletions(-) create mode 100644 plugin/src/main/java/com/flit/protoc/gen/server/jakarta/JakartaGenerator.java create mode 100644 plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jakarta/ContextGeneratorTest.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.java create mode 100644 plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt create mode 100644 plugin/src/test/resources/context.empty.jakarta.json create mode 100644 plugin/src/test/resources/context.missing.jakarta.json create mode 100644 plugin/src/test/resources/context.name.jakarta.json create mode 100644 plugin/src/test/resources/context.root.jakarta.json create mode 100644 plugin/src/test/resources/context.slash.jakarta.json create mode 100644 plugin/src/test/resources/helloworld.jakarta.json create mode 100644 plugin/src/test/resources/status.jakarta.json create mode 100644 runtime/jakarta/build.gradle create mode 100644 runtime/jakarta/src/main/java/com/flit/runtime/jakarta/FlitExceptionMapper.java create mode 100644 runtime/jakarta/src/main/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapper.java create mode 100644 runtime/jakarta/src/test/java/com/flit/runtime/jakarta/FlitExceptionMapperTest.java create mode 100644 runtime/jakarta/src/test/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapperTest.java diff --git a/README.md b/README.md index 8c310d5..357ab65 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ It supports the generation of Java based servers with the following flavours sup + [Spring Boot/Spring MVC](https://spring.io/projects/spring-boot "Spring Boot") + [Undertow](http://undertow.io/ "Undertow") + JAX-RS ([Jersey](https://eclipse-ee4j.github.io/jersey/), [Apache CFX](http://cxf.apache.org/)) ++ [Jakarta EE](https://jakarta.ee/ "Jakarta") ## Building & Running @@ -24,13 +25,14 @@ To test you will need an installation of the [protocol buffers compiler](https:/ The project is split into the following modules: -| Module | Description | -|:------------------|:------------------------------------------------------| -| `plugin` | The `protoc` plugin | -| `runtime:core` | Core functionality required by generated code | -| `runtime:jaxrs` | Runtime library for JAX-RS servers | -| `runtime:spring` | Runtime library for Spring MVC/Boot servers | -| `runtime:undertow`| Runtime library for Undertow servers | +| Module | Description | +|:-------------------|:----------------------------------------------| +| `plugin` | The `protoc` plugin | +| `runtime:core` | Core functionality required by generated code | +| `runtime:jakarta` | Runtime library for Jakarta servers | +| `runtime:jaxrs` | Runtime library for JAX-RS servers | +| `runtime:spring` | Runtime library for Spring MVC/Boot servers | +| `runtime:undertow` | Runtime library for Undertow servers | ### Build @@ -63,12 +65,12 @@ The plugin is executed as part of a protoc compilation step: 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 | -| `type` | Y | `enum[spring,undertow,boot,jaxrs]`| Type of target to generate | -| `context` | N | `string` | Base context for routing, default is `/twirp` | -| `request` | N | `string` | If the request parameter should pass to the service | +| Name | Required | Type | Description | +|:----------|:---------:|:-------------------------------------------|:-------------------------------------------------------| +| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc | +| `type` | Y | `enum[spring,undertow,boot,jakarta,jaxrs]` | Type of target to generate | +| `context` | N | `string` | Base context for routing, default is `/twirp` | +| `request` | N | `string` | If the request parameter should pass to the service | # Development diff --git a/build.gradle b/build.gradle index 3d123b7..c29b908 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,8 @@ allprojects { ext { // third party + jakartaservletapiVersion = "5.0.0" + jakartawsrsapiVersion = "3.0.0" javapoetVersion = "1.13.0" javaxservletapiVersion = "4.0.1" javaxwsrsapiVersion = "2.1.1" @@ -25,7 +27,8 @@ allprojects { // testing approvaltestsVersion = "18.5.0" javaparserVersion = "3.25.2" - jerseyCommonVersion = "2.22.2" + jerseyCommonJavaxVersion = "2.22.2" + jerseyCommonJakartaVersion = "3.1.1" junitJupiterVersion = "5.9.2" mockitoVersion = "5.2.0" } diff --git a/plugin/src/main/java/com/flit/protoc/Plugin.java b/plugin/src/main/java/com/flit/protoc/Plugin.java index d67b519..eeffe35 100644 --- a/plugin/src/main/java/com/flit/protoc/Plugin.java +++ b/plugin/src/main/java/com/flit/protoc/Plugin.java @@ -6,6 +6,7 @@ import com.flit.protoc.gen.Generator; import com.flit.protoc.gen.GeneratorException; +import com.flit.protoc.gen.server.jakarta.JakartaGenerator; import com.flit.protoc.gen.server.jaxrs.JaxrsGenerator; import com.flit.protoc.gen.server.spring.SpringGenerator; import com.flit.protoc.gen.server.undertow.UndertowGenerator; @@ -59,6 +60,8 @@ private Generator resolveGenerator(Map params) { return new UndertowGenerator(requestServices); case "jaxrs": return new JaxrsGenerator(requestServices); + case "jakarta": + return new JakartaGenerator(requestServices); default: throw new GeneratorException("Unknown server type: " + params.get(PARAM_TYPE).getValue()); } diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/JakartaGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/JakartaGenerator.java new file mode 100644 index 0000000..a3520f0 --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/JakartaGenerator.java @@ -0,0 +1,29 @@ +package com.flit.protoc.gen.server.jakarta; + +import static com.flit.protoc.gen.server.jakarta.RpcGenerator.HttpServletRequest; + +import com.flit.protoc.gen.server.BaseGenerator; +import com.flit.protoc.gen.server.BaseServerGenerator; +import com.flit.protoc.gen.server.TypeMapper; +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; +import com.squareup.javapoet.TypeName; +import java.util.List; + +public class JakartaGenerator extends BaseServerGenerator { + + public JakartaGenerator(List requestServices) { + super(requestServices); + } + + @Override + protected BaseGenerator getRpcGenerator(FileDescriptorProto proto, ServiceDescriptorProto service, + String context, TypeMapper mapper) { + return new RpcGenerator(proto, service, context, mapper, isRequestBasedClass(service)); + } + + @Override + protected TypeName getHttpRequestTypeName() { + return HttpServletRequest; + } +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java new file mode 100644 index 0000000..bc26e48 --- /dev/null +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java @@ -0,0 +1,129 @@ +package com.flit.protoc.gen.server.jakarta; + +import com.flit.protoc.gen.server.BaseGenerator; +import com.flit.protoc.gen.server.TypeMapper; +import com.flit.protoc.gen.server.Types; +import com.google.common.net.MediaType; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.TypeSpec.Builder; +import java.util.Collections; +import java.util.List; +import javax.lang.model.element.Modifier; + +public class RpcGenerator extends BaseGenerator { + + static final ClassName PATH = ClassName.bestGuess("jakarta.ws.rs.Path"); + static final ClassName POST = ClassName.bestGuess("jakarta.ws.rs.POST"); + static final ClassName CONTEXT = ClassName.bestGuess("jakarta.ws.rs.core.Context"); + static final ClassName HttpServletRequest = ClassName.bestGuess("jakarta.servlet.http.HttpServletRequest"); + static final ClassName HttpServletResponse = ClassName.bestGuess("jakarta.servlet.http.HttpServletResponse"); + + private final Builder rpcResource; + private final boolean passRequest; + + RpcGenerator( + DescriptorProtos.FileDescriptorProto proto, + DescriptorProtos.ServiceDescriptorProto service, + String context, + TypeMapper mapper, + boolean passRequest + ) { + super(proto, service, mapper); + String prefix = getContext(context); + this.passRequest = passRequest; + this.rpcResource = TypeSpec.classBuilder(getResourceName(service)) + .addModifiers(Modifier.PUBLIC) + .addAnnotation( + AnnotationSpec.builder(PATH).addMember("value", "$S", + prefix + "/" + (proto.hasPackage() ? proto.getPackage() + "." : "") + service + .getName()).build()); + addInstanceFields(); + addConstructor(); + service.getMethodList().forEach(this::addHandleMethod); + } + + private void addConstructor() { + rpcResource.addMethod(MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(getServiceInterface(), "service") + .addStatement("this.service = service").build()); + } + + private void addHandleMethod(MethodDescriptorProto mdp) { + ClassName inputType = mapper.get(mdp.getInputType()); + ClassName outputType = mapper.get(mdp.getOutputType()); + rpcResource.addMethod(MethodSpec.methodBuilder("handle" + mdp.getName()) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(POST) + .addAnnotation(AnnotationSpec.builder(PATH) + .addMember("value", "$S", "/" + mdp.getName()) + .build()) + .addParameter(ParameterSpec.builder(HttpServletRequest, "request") + .addAnnotation(CONTEXT).build()) + .addParameter(ParameterSpec.builder(HttpServletResponse, "response") + .addAnnotation(CONTEXT).build()) + .addException(Types.Exception) + .addStatement("boolean json = false") + .addStatement("final $T data", inputType) + .beginControlFlow("if (request.getContentType().equals($S))", MediaType.PROTOBUF.toString()) + .addStatement("data = $T.parseFrom(request.getInputStream())", inputType) + .nextControlFlow("else if (request.getContentType().startsWith($S))", "application/json") + .addStatement("json = true") + .addStatement("$T.Builder builder = $T.newBuilder()", inputType, inputType) + .addStatement("$T.parser().merge(new $T(request.getInputStream(), $T.UTF_8), builder)", + Types.JsonFormat, + Types.InputStreamReader, + Types.StandardCharsets) + .addStatement("data = builder.build()") + .nextControlFlow("else") + .addStatement("response.setStatus(415)") + .addStatement("response.flushBuffer()") + .addStatement("return") + .endControlFlow() + // route to the service + .addStatement(getRouteToService(), outputType, mdp.getName()) + .addStatement("response.setStatus(200)") + // send the response + .beginControlFlow("if (json)") + .addStatement("response.setContentType($S)", MediaType.JSON_UTF_8.toString()) + .addStatement("response.getOutputStream().write($T.printer().omittingInsignificantWhitespace().print(retval).getBytes($T.UTF_8))", + Types.JsonFormat, + Types.StandardCharsets) + .nextControlFlow("else") + .addStatement("response.setContentType($S)", MediaType.PROTOBUF.toString()) + .addStatement("retval.writeTo(response.getOutputStream())") + .endControlFlow() + .addStatement("response.flushBuffer()") + .build()); + } + + private String getRouteToService() { + if (passRequest) { + return "$T retval = service.handle$L(request, data)"; + } else { + return "$T retval = service.handle$L(data)"; + } + } + + private ClassName getResourceName(DescriptorProtos.ServiceDescriptorProto service) { + return ClassName.get(javaPackage, "Rpc" + service.getName() + "Resource"); + } + + private void addInstanceFields() { + rpcResource.addField(FieldSpec.builder(getServiceInterface(), "service") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()); + } + + @Override + public List getFiles() { + return Collections.singletonList(toFile(getResourceName(service), rpcResource.build())); + } +} diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index d4819d6..8e1f3c4 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -20,14 +20,12 @@ public class RpcGenerator extends BaseGenerator { - public static final ClassName PATH = ClassName.bestGuess("javax.ws.rs.Path"); - public static final ClassName POST = ClassName.bestGuess("javax.ws.rs.POST"); - public static final ClassName PRODUCES = ClassName.bestGuess("javax.ws.rs.Produces"); - public static final ClassName CONSUMES = ClassName.bestGuess("javax.ws.rs.Consumes"); - public static final ClassName CONTEXT = ClassName.bestGuess("javax.ws.rs.core.Context"); - public static final ClassName HttpServletRequest = ClassName.bestGuess("javax.servlet.http.HttpServletRequest"); - public static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); - private final String context; + static final ClassName PATH = ClassName.bestGuess("javax.ws.rs.Path"); + static final ClassName POST = ClassName.bestGuess("javax.ws.rs.POST"); + static final ClassName CONTEXT = ClassName.bestGuess("javax.ws.rs.core.Context"); + static final ClassName HttpServletRequest = ClassName.bestGuess("javax.servlet.http.HttpServletRequest"); + static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); + private final Builder rpcResource; private final boolean passRequest; @@ -39,13 +37,13 @@ public class RpcGenerator extends BaseGenerator { boolean passRequest ) { super(proto, service, mapper); - this.context = getContext(context); + String prefix = getContext(context); this.passRequest = passRequest; this.rpcResource = TypeSpec.classBuilder(getResourceName(service)) .addModifiers(Modifier.PUBLIC) .addAnnotation( AnnotationSpec.builder(PATH).addMember("value", "$S", - this.context + "/" + (proto.hasPackage() ? proto.getPackage() + "." : "") + service + prefix + "/" + (proto.hasPackage() ? proto.getPackage() + "." : "") + service .getName()).build()); addInstanceFields(); addConstructor(); 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 4668311..f830a51 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 @@ -14,11 +14,11 @@ class RpcGenerator extends BaseGenerator { - public static final ClassName RestController = ClassName.bestGuess("org.springframework.web.bind.annotation.RestController"); - public static final ClassName Autowired = ClassName.bestGuess("org.springframework.beans.factory.annotation.Autowired"); - public static final ClassName PostMapping = ClassName.bestGuess("org.springframework.web.bind.annotation.PostMapping"); - public static final ClassName HttpServletRequest = ClassName.bestGuess("javax.servlet.http.HttpServletRequest"); - public static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); + static final ClassName RestController = ClassName.bestGuess("org.springframework.web.bind.annotation.RestController"); + static final ClassName Autowired = ClassName.bestGuess("org.springframework.beans.factory.annotation.Autowired"); + static final ClassName PostMapping = ClassName.bestGuess("org.springframework.web.bind.annotation.PostMapping"); + static final ClassName HttpServletRequest = ClassName.bestGuess("javax.servlet.http.HttpServletRequest"); + static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); private final String context; private final TypeSpec.Builder rpcController; diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/ContextGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/ContextGeneratorTest.java new file mode 100644 index 0000000..676661b --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/ContextGeneratorTest.java @@ -0,0 +1,67 @@ +package com.flit.protoc.gen.server.jakarta; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; + +/** + * Tests the generation of a service that has core definition imported from another file + */ +public class ContextGeneratorTest extends BaseGeneratorTest { + + @Test + public void test_GenerateWithMissingRoot() throws Exception { + test_Route("context.missing.jakarta.json", "/twirp/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithEmptyRoot() throws Exception { + test_Route("context.empty.jakarta.json", "/twirp/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithSlashOnlyRoot() throws Exception { + test_Route("context.slash.jakarta.json", "/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithSlashRoot() throws Exception { + test_Route("context.root.jakarta.json", "/root/com.example.context.NullService"); + } + + @Test + public void test_GenerateWithNameRoot() throws Exception { + test_Route("context.name.jakarta.json", "/fibble/com.example.context.NullService"); + } + + private void test_Route(String file, String route) throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson(file); + + Plugin plugin = new Plugin(request); + PluginProtos.CodeGeneratorResponse response = plugin.process(); + + assertNotNull(response); + assertEquals(2, response.getFileCount()); + + Map files = response.getFileList() + .stream() + .collect(Collectors + .toMap(File::getName, Function.identity())); + + assertTrue(files.containsKey("com/example/context/rpc/RpcNullService.java")); + assertTrue(files.containsKey("com/example/context/rpc/RpcNullServiceResource.java")); + + assertTrue(files.get("com/example/context/rpc/RpcNullServiceResource.java") + .getContent() + .contains(String.format("@Path(\"%s\")", route))); + } +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.java new file mode 100644 index 0000000..b719eac --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.java @@ -0,0 +1,47 @@ +package com.flit.protoc.gen.server.jakarta; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import org.approvaltests.Approvals; +import org.junit.jupiter.api.Test; + +public class HelloworldGeneratorTest extends BaseGeneratorTest { + + @Test + public void test_Generate() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.jakarta.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/RpcHelloWorldResource.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); + response.getFileList().forEach(BaseGeneratorTest::assertParses); + } + + @Test + public void test_GenerateWithRequest() throws Exception { + PluginProtos.CodeGeneratorRequest request = loadJson("helloworld.jakarta.json", "target=server,type=jakarta,request=HelloWorld"); + + 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/RpcHelloWorldResource.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); + response.getFileList().forEach(BaseGeneratorTest::assertParses); + } +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt new file mode 100644 index 0000000..21cd4d1 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt @@ -0,0 +1,89 @@ +[0] = package com.example.helloworld; + +public interface RpcHelloWorld { + Helloworld.HelloResp handleHello(Helloworld.HelloReq in); + + Helloworld.HelloResp handleHelloAgain(Helloworld.HelloReq in); +} + +[1] = package com.example.helloworld; + +import com.google.protobuf.util.JsonFormat; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; + +@Path("/twirp/com.example.helloworld.HelloWorld") +public class RpcHelloWorldResource { + private final RpcHelloWorld service; + + public RpcHelloWorldResource(RpcHelloWorld service) { + this.service = service; + } + + @POST + @Path("/Hello") + public void handleHello(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHello(data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } + + @POST + @Path("/HelloAgain") + public void handleHelloAgain(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHelloAgain(data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } +} + diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt new file mode 100644 index 0000000..b97fe8e --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -0,0 +1,91 @@ +[0] = package com.example.helloworld; + +import jakarta.servlet.http.HttpServletRequest; + +public interface RpcHelloWorld { + Helloworld.HelloResp handleHello(HttpServletRequest request, Helloworld.HelloReq in); + + Helloworld.HelloResp handleHelloAgain(HttpServletRequest request, Helloworld.HelloReq in); +} + +[1] = package com.example.helloworld; + +import com.google.protobuf.util.JsonFormat; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; + +@Path("/twirp/com.example.helloworld.HelloWorld") +public class RpcHelloWorldResource { + private final RpcHelloWorld service; + + public RpcHelloWorldResource(RpcHelloWorld service) { + this.service = service; + } + + @POST + @Path("/Hello") + public void handleHello(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHello(request, data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } + + @POST + @Path("/HelloAgain") + public void handleHelloAgain(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Helloworld.HelloReq data; + if (request.getContentType().equals("application/protobuf")) { + data = Helloworld.HelloReq.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + Helloworld.HelloResp retval = service.handleHelloAgain(request, data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } +} + diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.java b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.java new file mode 100644 index 0000000..07d3ffb --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.java @@ -0,0 +1,36 @@ +package com.flit.protoc.gen.server.jakarta; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.flit.protoc.Plugin; +import com.flit.protoc.gen.BaseGeneratorTest; +import com.google.protobuf.compiler.PluginProtos; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.File; +import org.approvaltests.Approvals; +import org.junit.jupiter.api.Test; + +/** + * 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.jakarta.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/RpcStatusResource.java"); + + Approvals.verifyAll("", response.getFileList().stream().map(File::getContent).collect(toList())); + response.getFileList().forEach(BaseGeneratorTest::assertParses); + } + +} diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt new file mode 100644 index 0000000..41d4639 --- /dev/null +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt @@ -0,0 +1,57 @@ +[0] = package com.example.helloworld; + +public interface RpcStatus { + StatusOuterClass.StatusResponse handleGetStatus(Core.Empty in); +} + +[1] = package com.example.helloworld; + +import com.google.protobuf.util.JsonFormat; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import java.io.InputStreamReader; +import java.lang.Exception; +import java.nio.charset.StandardCharsets; + +@Path("/twirp/com.example.helloworld.Status") +public class RpcStatusResource { + private final RpcStatus service; + + public RpcStatusResource(RpcStatus service) { + this.service = service; + } + + @POST + @Path("/GetStatus") + public void handleGetStatus(@Context HttpServletRequest request, + @Context HttpServletResponse response) throws Exception { + boolean json = false; + final Core.Empty data; + if (request.getContentType().equals("application/protobuf")) { + data = Core.Empty.parseFrom(request.getInputStream()); + } else if (request.getContentType().startsWith("application/json")) { + json = true; + Core.Empty.Builder builder = Core.Empty.newBuilder(); + JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + data = builder.build(); + } else { + response.setStatus(415); + response.flushBuffer(); + return; + } + StatusOuterClass.StatusResponse retval = service.handleGetStatus(data); + response.setStatus(200); + if (json) { + response.setContentType("application/json; charset=utf-8"); + response.getOutputStream().write(JsonFormat.printer().omittingInsignificantWhitespace().print(retval).getBytes(StandardCharsets.UTF_8)); + } else { + response.setContentType("application/protobuf"); + retval.writeTo(response.getOutputStream()); + } + response.flushBuffer(); + } +} + diff --git a/plugin/src/test/resources/context.empty.jakarta.json b/plugin/src/test/resources/context.empty.jakarta.json new file mode 100644 index 0000000..08e10d9 --- /dev/null +++ b/plugin/src/test/resources/context.empty.jakarta.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jakarta,context=", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.missing.jakarta.json b/plugin/src/test/resources/context.missing.jakarta.json new file mode 100644 index 0000000..08e10d9 --- /dev/null +++ b/plugin/src/test/resources/context.missing.jakarta.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jakarta,context=", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.name.jakarta.json b/plugin/src/test/resources/context.name.jakarta.json new file mode 100644 index 0000000..c5941cf --- /dev/null +++ b/plugin/src/test/resources/context.name.jakarta.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jakarta,context=fibble", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.root.jakarta.json b/plugin/src/test/resources/context.root.jakarta.json new file mode 100644 index 0000000..2e00fdb --- /dev/null +++ b/plugin/src/test/resources/context.root.jakarta.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jakarta,context=/root", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/context.slash.jakarta.json b/plugin/src/test/resources/context.slash.jakarta.json new file mode 100644 index 0000000..45bbad1 --- /dev/null +++ b/plugin/src/test/resources/context.slash.jakarta.json @@ -0,0 +1,29 @@ +{ + "fileToGenerate": ["context.proto"], + "parameter": "target=server,type=jakarta,context=/", + "compilerVersion": { + "major": 3, + "minor": 5, + "patch": 1, + "suffix": "" + }, + "protoFile": [{ + "name": "context.proto", + "package": "com.example.context", + "messageType": [{ + "name": "Empty" + }], + "service": [{ + "name": "NullService", + "method": [{ + "name": "SayNull", + "inputType": ".com.example.context.Empty", + "outputType": ".com.example.context.Empty" + }] + }], + "options": { + "javaPackage": "com.example.context.rpc" + }, + "syntax": "proto3" + }] +} diff --git a/plugin/src/test/resources/helloworld.jakarta.json b/plugin/src/test/resources/helloworld.jakarta.json new file mode 100644 index 0000000..36986ab --- /dev/null +++ b/plugin/src/test/resources/helloworld.jakarta.json @@ -0,0 +1,49 @@ +{ + "fileToGenerate": ["helloworld.proto"], + "parameter": "target=server,type=jakarta", + "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.jakarta.json b/plugin/src/test/resources/status.jakarta.json new file mode 100644 index 0000000..2bd0ee0 --- /dev/null +++ b/plugin/src/test/resources/status.jakarta.json @@ -0,0 +1,79 @@ +{ + "fileToGenerate": ["core.proto", "status.proto"], + "parameter": "target=server,type=jakarta", + "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/runtime/jakarta/build.gradle b/runtime/jakarta/build.gradle new file mode 100644 index 0000000..56c2670 --- /dev/null +++ b/runtime/jakarta/build.gradle @@ -0,0 +1,20 @@ +dependencies { + implementation(project(':runtime:core')) + implementation("jakarta.servlet:jakarta.servlet-api:$jakartaservletapiVersion") + implementation("jakarta.ws.rs:jakarta.ws.rs-api:$jakartawsrsapiVersion") + implementation("org.slf4j:slf4j-api:$slf4jVersion") + + testImplementation("org.glassfish.jersey.core:jersey-common:$jerseyCommonJakartaVersion") + testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion") +} + +publishing { + publications { + maven(MavenPublication) { + artifactId = 'flit-jakarta-runtime' + version = "1.3" + + from components.java + } + } +} diff --git a/runtime/jakarta/src/main/java/com/flit/runtime/jakarta/FlitExceptionMapper.java b/runtime/jakarta/src/main/java/com/flit/runtime/jakarta/FlitExceptionMapper.java new file mode 100644 index 0000000..865ad18 --- /dev/null +++ b/runtime/jakarta/src/main/java/com/flit/runtime/jakarta/FlitExceptionMapper.java @@ -0,0 +1,41 @@ +package com.flit.runtime.jakarta; + +import com.flit.runtime.FlitException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +public class FlitExceptionMapper implements ExceptionMapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionMapper.class); + + @Context + private HttpServletRequest request; + + @Override + public Response toResponse(FlitException exception) { + LOGGER.error("Flit exception: request = {}, method = {}, code = {}, msg = {}", + request.getRequestURI(), request.getMethod(), exception.getErrorCode(), + exception.getMessage(), exception); + + Map response = new HashMap<>(); + response.put("code", exception.getErrorCode().getErrorCode()); + response.put("msg", exception.getMessage()); + + if (exception.hasMeta()) { + response.put("meta", exception.getMeta()); + } + return Response.status(exception.getErrorCode().getHttpStatus()) + .type(MediaType.APPLICATION_JSON) + .entity(response) + .build(); + } +} diff --git a/runtime/jakarta/src/main/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapper.java b/runtime/jakarta/src/main/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapper.java new file mode 100644 index 0000000..7052585 --- /dev/null +++ b/runtime/jakarta/src/main/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapper.java @@ -0,0 +1,36 @@ +package com.flit.runtime.jakarta; + +import com.flit.runtime.ErrorCode; +import com.google.protobuf.InvalidProtocolBufferException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InvalidProtobufExceptionMapper implements + ExceptionMapper { + + private static final Logger LOGGER = LoggerFactory + .getLogger(InvalidProtobufExceptionMapper.class); + + @Context private HttpServletRequest request; + + @Override + public Response toResponse(InvalidProtocolBufferException exception) { + LOGGER.error("InvalidProtocolBufferException: request = {}, method = {}, msg= {}", + request.getRequestURI(), request.getMethod(), exception.getMessage(), exception); + + Map response = new HashMap<>(); + response.put("code", ErrorCode.INVALID_ARGUMENT.getErrorCode()); + response.put("msg", exception.getMessage()); + return Response.status(ErrorCode.INVALID_ARGUMENT.getHttpStatus()) + .type(MediaType.APPLICATION_JSON) + .entity(response) + .build(); + } +} diff --git a/runtime/jakarta/src/test/java/com/flit/runtime/jakarta/FlitExceptionMapperTest.java b/runtime/jakarta/src/test/java/com/flit/runtime/jakarta/FlitExceptionMapperTest.java new file mode 100644 index 0000000..aaf6306 --- /dev/null +++ b/runtime/jakarta/src/test/java/com/flit/runtime/jakarta/FlitExceptionMapperTest.java @@ -0,0 +1,40 @@ +package com.flit.runtime.jakarta; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.flit.runtime.ErrorCode; +import com.flit.runtime.FlitException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class FlitExceptionMapperTest { + + @Mock + private HttpServletRequest request; + + @InjectMocks + private FlitExceptionMapper flitExceptionMapper = new FlitExceptionMapper(); + + @Test + void testToResponse() { + FlitException flit = FlitException.builder() + .withCause(new RuntimeException("Failed")) + .withErrorCode(ErrorCode.INTERNAL) + .withMessage("with this message") + .build(); + Response response = flitExceptionMapper.toResponse(flit); + assertEquals(response.getStatus(), Status.INTERNAL_SERVER_ERROR.getStatusCode()); + Map expectedResult = Map.of("msg", "with this message", "code", "internal"); + assertEquals(response.getEntity(), expectedResult); + assertEquals(response.getMediaType(), MediaType.APPLICATION_JSON_TYPE); + } +} \ No newline at end of file diff --git a/runtime/jakarta/src/test/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapperTest.java b/runtime/jakarta/src/test/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapperTest.java new file mode 100644 index 0000000..9e26163 --- /dev/null +++ b/runtime/jakarta/src/test/java/com/flit/runtime/jakarta/InvalidProtobufExceptionMapperTest.java @@ -0,0 +1,36 @@ +package com.flit.runtime.jakarta; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.protobuf.InvalidProtocolBufferException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class InvalidProtobufExceptionMapperTest { + + @Mock + private HttpServletRequest request; + + @InjectMocks + private InvalidProtobufExceptionMapper mapper = new InvalidProtobufExceptionMapper(); + + @Test + void testToResponse() { + InvalidProtocolBufferException exception = new InvalidProtocolBufferException( + "something happened"); + Response response = mapper.toResponse(exception); + assertEquals(response.getStatus(), 400); + Map expectedResult = Map + .of("msg", "something happened", "code", "invalid_argument"); + assertEquals(response.getEntity(), expectedResult); + assertEquals(response.getMediaType(), MediaType.APPLICATION_JSON_TYPE); + } +} diff --git a/runtime/jaxrs/build.gradle b/runtime/jaxrs/build.gradle index 089c4a9..b7f49de 100644 --- a/runtime/jaxrs/build.gradle +++ b/runtime/jaxrs/build.gradle @@ -4,7 +4,7 @@ dependencies { implementation("javax.ws.rs:javax.ws.rs-api:$javaxwsrsapiVersion") implementation("org.slf4j:slf4j-api:$slf4jVersion") - testImplementation("org.glassfish.jersey.core:jersey-common:$jerseyCommonVersion") + testImplementation("org.glassfish.jersey.core:jersey-common:$jerseyCommonJavaxVersion") testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion") } diff --git a/settings.gradle b/settings.gradle index 6865aac..0b7bbe3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,4 @@ include 'runtime:core' include 'runtime:spring' include 'runtime:undertow' include 'runtime:jaxrs' - +include 'runtime:jakarta' From fccf255db04f7e08eb7d7654b8a26687469cadce Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Tue, 2 May 2023 15:11:03 -0700 Subject: [PATCH 25/44] update readme with better usage and development info (#12) --- README.md | 201 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 123 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 357ab65..5fa50da 100644 --- a/README.md +++ b/README.md @@ -8,50 +8,108 @@ It supports the generation of Java based servers with the following flavours sup + [Spring Boot/Spring MVC](https://spring.io/projects/spring-boot "Spring Boot") + [Undertow](http://undertow.io/ "Undertow") + JAX-RS ([Jersey](https://eclipse-ee4j.github.io/jersey/), [Apache CFX](http://cxf.apache.org/)) -+ [Jakarta EE](https://jakarta.ee/ "Jakarta") - -## Building & Running - -### Requirements - -The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/#zulu "JDK Downloads") version 11. - -The build uses gradle to generate the artifacts. No installation is required as the project uses the -[gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html "gradle wrapper") setup. ++ [Jakarta EE](https://jakarta.ee/ "Jakarta") + +# Contents + + + + + +- [Using](#using) + - [Runtime libraries](#runtime-libraries) + - [Protoc plugin](#protoc-plugin) + - [via gradle](#via-gradle) + - [via protoc](#via-protoc) +- [Development](#development) + - [Requirements](#requirements) + - [Modules](#modules) + - [Gradle commands](#gradle-commands) + - [Remote Debug](#remote-debug) +- [Guides](#guides) + + + +## Using + +### Runtime libraries + +Follow the [Working with the Gradle registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry) docs to set up authentication with GitHub packages. + +[Published](https://github.com/orgs/github/packages?repo_name=flit) runtime libraries example Gradle setup: +```groovy +repositories { + maven { + name = 'GithubPackages' + url 'https://maven.pkg.github.com/github/flit' + credentials { + username = project.findProperty('gpr.user') ?: System.getenv('GITHUB_ACTOR') + password = project.findProperty('gpr.key') ?: System.getenv('GITHUB_TOKEN') + } + content { + includeGroup('com.flit') + } + } +} -To test you will need an installation of the [protocol buffers compiler](https://github.com/protocolbuffers/protobuf/releases "protobuf releases"). +dependencies { + implementation("com.flit:flit-core-runtime:") + implementation("com.flit:flit-spring-runtime:") + implementation("com.flit:flit-jaxrs-runtime:") + implementation("com.flit:flit-jakarta-runtime:") + implementation("com.flit:flit-undertow-runtime:") +} +``` -### Modules +### Protoc plugin -The project is split into the following modules: - -| Module | Description | -|:-------------------|:----------------------------------------------| -| `plugin` | The `protoc` plugin | -| `runtime:core` | Core functionality required by generated code | -| `runtime:jakarta` | Runtime library for Jakarta servers | -| `runtime:jaxrs` | Runtime library for JAX-RS servers | -| `runtime:spring` | Runtime library for Spring MVC/Boot servers | -| `runtime:undertow` | Runtime library for Undertow servers | +Plugin `com.flit.flit-plugin` shadow jar can be downloaded from [here](https://github.com/github/flit/packages/1832284). +The flit plugin accepts the following plugin parameters: -### Build +| Name | Required | Type | Description | +|:----------|:---------:|:-------------------------------------------|:-------------------------------------------------------| +| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc | +| `type` | Y | `enum[spring,undertow,boot,jakarta,jaxrs]` | Type of target to generate | +| `context` | N | `string` | Base context for routing, default is `/twirp` | +| `request` | N | `string` | If the request parameter should pass to the service | -To build the various components, run the following: +#### via gradle - git clone git@github.com:github/flit.git - cd flit - ./gradlew clean build +The plugin can be called from the Gradle protobuf plugin via additional configuration. For example: -### Installation +```groovy +protobuf { + plugins { + flit { + path = "${projectDir}/script/protoc-gen-flit" + } + } -Currently, the run script only supports *nix but the run script should be fairly easy to migrate to windows. + generateProtoTasks { + ofSourceSet('main')*.plugins { + flit { + option 'target=server' + option 'type=spring' + option 'request=Service' + } + } + } +} +``` +```shell +#!/usr/bin/env bash -After building: +DIR=$(dirname "$0") - cp plugin/build/libs/plugin-all.jar /code/java-project/scripts/ +JAR=$(ls -c ${DIR}/plugin-*-all.jar | head -1) +java ${FLIT_JAVA_OPTS} -jar $JAR $@ +``` +Where the plugin jar is located in the same directly as the script. -## Running +#### via protoc The plugin is executed as part of a protoc compilation step: @@ -61,59 +119,46 @@ The plugin is executed as part of a protoc compilation step: --flit_out=target=server,type=undertow:../java \ ./haberdasher.proto -### Options +## Development -The flit plugin accepts the following plugin parameters: +### Requirements -| Name | Required | Type | Description | -|:----------|:---------:|:-------------------------------------------|:-------------------------------------------------------| -| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc | -| `type` | Y | `enum[spring,undertow,boot,jakarta,jaxrs]` | Type of target to generate | -| `context` | N | `string` | Base context for routing, default is `/twirp` | -| `request` | N | `string` | If the request parameter should pass to the service | +The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/#zulu "JDK Downloads") version 11. -# Development +The build uses gradle to generate the artifacts. No installation is required as the project uses the +[gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html "gradle wrapper") setup. -All development is done in Java using JDK 8 (as mentioned above). +Optional: to test you will need an installation of the [protocol buffers compiler](https://github.com/protocolbuffers/protobuf/releases "protobuf releases"). -Remote debugging can be performed as follows: +### Modules - export FLIT_JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005,quiet=y" - protoc \ - --proto_path=. \ - --java_out=../java \ - --flit_out=target=server,type=undertow:../java \ - ./haberdasher.proto - -When running with the above options, the generator will enable a remote java debug session on port 5005. This is useful -for debugging a full generation step. - -## Test Fixture Generation - -The test resources contains a fake plugin that simply dumps the binary request to a file called `file.bin`. This utility -can be used to generate test fixtures which can be fed to tests to drive plugin generation, for example: - - $ protoc \ - --plugin=${PWD}/protoc-gen-dump \ - --dump_out=target=server,type=undertow:../java \ - ./helloworld.proto - $ mv file.bin helloworld.bin - -This can be run from the resources directory to generate a `CodeGeneratorRequest` protobuf file, which can then be read -by tests: - - PluginProtos.CodeGeneratorRequest request = null; - try (InputStream is = this.getClass().getClassLoader().getResource("helloworld.bin").openStream()) { - request = PluginProtos.CodeGeneratorRequest - .newBuilder() - .mergeFrom(is) - .build(); - } +The project is split into the following modules: + +| Module | Description | +|:-------------------|:----------------------------------------------| +| `plugin` | The `protoc` plugin | +| `runtime:core` | Core functionality required by generated code | +| `runtime:jakarta` | Runtime library for Jakarta servers | +| `runtime:jaxrs` | Runtime library for JAX-RS servers | +| `runtime:spring` | Runtime library for Spring MVC/Boot servers | +| `runtime:undertow` | Runtime library for Undertow servers | + +### Gradle commands + +* clean `./gradlew clean` +* build `./gradlew build` +* test `./gradlew test` +* publish `./gradlew publish` + +### Remote Debug + +Use remote JVM debugging by setting: + +```shell +export FLIT_JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005,quiet=y" +``` - Plugin plugin = new Plugin(request); - plugin.process(); - -# Guides +## Guides | Platform | Document | |:----------|:--------------------------------------| From 61a60892e0b7f952c2742db3307fedfa73f847db Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Tue, 2 May 2023 15:25:57 -0700 Subject: [PATCH 26/44] change core-runtime dependencies from impl to api (#13) --- README.md | 6 +++++- runtime/jakarta/build.gradle | 2 +- runtime/jaxrs/build.gradle | 2 +- runtime/spring/build.gradle | 2 +- runtime/undertow/build.gradle | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5fa50da..8959815 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,10 @@ The plugin can be called from the Gradle protobuf plugin via additional configur ```groovy protobuf { + protoc { + artifact = "com.google.protobuf:protoc:$protocVersion" + } + plugins { flit { path = "${projectDir}/script/protoc-gen-flit" @@ -104,7 +108,7 @@ protobuf { DIR=$(dirname "$0") -JAR=$(ls -c ${DIR}/plugin-*-all.jar | head -1) +JAR=$(ls -c ${DIR}/flit-plugin-*.jar | head -1) java ${FLIT_JAVA_OPTS} -jar $JAR $@ ``` Where the plugin jar is located in the same directly as the script. diff --git a/runtime/jakarta/build.gradle b/runtime/jakarta/build.gradle index 56c2670..080ee66 100644 --- a/runtime/jakarta/build.gradle +++ b/runtime/jakarta/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation(project(':runtime:core')) + api(project(':runtime:core')) implementation("jakarta.servlet:jakarta.servlet-api:$jakartaservletapiVersion") implementation("jakarta.ws.rs:jakarta.ws.rs-api:$jakartawsrsapiVersion") implementation("org.slf4j:slf4j-api:$slf4jVersion") diff --git a/runtime/jaxrs/build.gradle b/runtime/jaxrs/build.gradle index b7f49de..732ac47 100644 --- a/runtime/jaxrs/build.gradle +++ b/runtime/jaxrs/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation(project(':runtime:core')) + api(project(':runtime:core')) implementation("javax.servlet:javax.servlet-api:$javaxservletapiVersion") implementation("javax.ws.rs:javax.ws.rs-api:$javaxwsrsapiVersion") implementation("org.slf4j:slf4j-api:$slf4jVersion") diff --git a/runtime/spring/build.gradle b/runtime/spring/build.gradle index 6478bfe..8f3296a 100644 --- a/runtime/spring/build.gradle +++ b/runtime/spring/build.gradle @@ -1,5 +1,5 @@ dependencies { - compileOnly(project(':runtime:core')) + api(project(':runtime:core')) compileOnly("org.slf4j:slf4j-api:$slf4jVersion") compileOnly("javax.servlet:javax.servlet-api:$javaxservletapiVersion") compileOnly("org.springframework:spring-webmvc:$springVersion") diff --git a/runtime/undertow/build.gradle b/runtime/undertow/build.gradle index ddcb74f..a5f9f6a 100644 --- a/runtime/undertow/build.gradle +++ b/runtime/undertow/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation(project(':runtime:core')) + api(project(':runtime:core')) compileOnly("org.slf4j:slf4j-api:$slf4jVersion") compileOnly("io.undertow:undertow-core:$undertowVersion") } From 4c6a6886f4011e65484be7054994897ddfc6af0b Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Mon, 31 Jul 2023 11:58:03 -0700 Subject: [PATCH 27/44] upgrade to spring-framework 6, java 17, and bump version to 2.0.0 (#14) * bump all versions to 2.0 because of multiple breaking changes * upgrade most dependencies * spring-framework required changes from javax to jakarta * change application.properties to application.yaml * compile target to version 17 because of spring requirement * fix actions target to 17 * bump gradle version to 8.2.1 * add `junit-platform-launcher` in prep for gradle 9 * add package-info for kotlin because why not --- .github/workflows/build.yaml | 6 +- .github/workflows/codeql-analysis.yml | 6 + .github/workflows/publish.yaml | 4 +- README.md | 2 +- build.gradle | 23 +- gradle/wrapper/gradle-wrapper.jar | Bin 56172 -> 61608 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 292 +++++++++++------- gradlew.bat | 56 ++-- plugin/build.gradle | 2 +- .../gen/server/spring/RpcGenerator.java | 4 +- ...ldGeneratorTest.test_Generate.approved.txt | 4 +- ...Test.test_GenerateWithRequest.approved.txt | 6 +- ...usGeneratorTest.test_Generate.approved.txt | 4 +- runtime/core/build.gradle | 2 +- runtime/jakarta/build.gradle | 2 +- runtime/jaxrs/build.gradle | 2 +- runtime/spring/build.gradle | 4 +- .../runtime/spring/FlitExceptionHandler.java | 10 +- .../spring/FlitHandlerExceptionResolver.java | 10 +- .../com/flit/runtime/spring/package-info.java | 6 + .../src/main/resources/application.properties | 3 - .../src/main/resources/application.yaml | 3 + runtime/undertow/build.gradle | 2 +- 24 files changed, 272 insertions(+), 184 deletions(-) create mode 100644 runtime/spring/src/main/java/com/flit/runtime/spring/package-info.java delete mode 100644 runtime/spring/src/main/resources/application.properties create mode 100644 runtime/spring/src/main/resources/application.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 07dcc48..4723b46 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -17,13 +17,11 @@ jobs: packages: read steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'zulu' - - name: Build with Gradle - uses: gradle/gradle-build-action@v2 - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 20cc662..8d626a1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,6 +41,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'zulu' + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 21cc611..af4fa53 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -15,10 +15,8 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'zulu' - - name: Publish with Gradle - uses: gradle/gradle-build-action@v2 - name: Publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 8959815..9f634b6 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ The plugin is executed as part of a protoc compilation step: ### Requirements -The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/#zulu "JDK Downloads") version 11. +The build has been tested with [Zulu's OpenJDK](https://www.azul.com/downloads/#zulu "JDK Downloads") version 17. The build uses gradle to generate the artifacts. No installation is required as the project uses the [gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html "gradle wrapper") setup. diff --git a/build.gradle b/build.gradle index c29b908..77517fd 100644 --- a/build.gradle +++ b/build.gradle @@ -14,29 +14,29 @@ allprojects { ext { // third party - jakartaservletapiVersion = "5.0.0" - jakartawsrsapiVersion = "3.0.0" + jakartaservletapiVersion = "6.0.0" + jakartawsrsapiVersion = "3.1.0" javapoetVersion = "1.13.0" javaxservletapiVersion = "4.0.1" javaxwsrsapiVersion = "2.1.1" - protobufVersion = "3.22.2" + protobufVersion = "3.23.4" slf4jVersion = "1.7.36" - springVersion = "5.3.26" + springVersion = "6.0.11" undertowVersion = "2.3.5.Final" // testing - approvaltestsVersion = "18.5.0" - javaparserVersion = "3.25.2" + approvaltestsVersion = "18.7.1" + javaparserVersion = "3.25.4" jerseyCommonJavaxVersion = "2.22.2" - jerseyCommonJakartaVersion = "3.1.1" - junitJupiterVersion = "5.9.2" - mockitoVersion = "5.2.0" + jerseyCommonJakartaVersion = "3.1.3" + junitJupiterVersion = "5.10.0" + mockitoVersion = "5.4.0" } java { group = "com.flit" - targetCompatibility = JavaVersion.VERSION_11 - sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_17 withSourcesJar() } @@ -50,6 +50,7 @@ allprojects { dependencies { testImplementation(platform("org.junit:junit-bom:$junitJupiterVersion")) testImplementation('org.junit.jupiter:junit-jupiter') + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } publishing { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 28861d273a5d270fd8f65dd74570c17c9c507736..ccebba7710deaf9f98673a68957ea02138b60d0a 100644 GIT binary patch delta 53255 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 delta 48047 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e1bef7e..17a8ddc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d..79a61d4 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright Š 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions ÂĢ$varÂģ, ÂĢ${var}Âģ, ÂĢ${var:-default}Âģ, ÂĢ${var+SET}Âģ, +# ÂĢ${var#prefix}Âģ, ÂĢ${var%suffix}Âģ, and ÂĢ$( cmd )Âģ; +# * compound commands having a testable exit status, especially ÂĢcaseÂģ; +# * various built-in commands including ÂĢcommandÂģ, ÂĢsetÂģ, and ÂĢulimitÂģ. +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -89,84 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -9,19 +25,23 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,38 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/plugin/build.gradle b/plugin/build.gradle index 6371af6..cd54ba7 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -23,7 +23,7 @@ publishing { publications { shadow(MavenPublication) { publication -> artifactId = 'flit-plugin' - version = '1.3' + version = '2.0' project.shadow.component(publication) } } 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 f830a51..facfe76 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 @@ -17,8 +17,8 @@ class RpcGenerator extends BaseGenerator { static final ClassName RestController = ClassName.bestGuess("org.springframework.web.bind.annotation.RestController"); static final ClassName Autowired = ClassName.bestGuess("org.springframework.beans.factory.annotation.Autowired"); static final ClassName PostMapping = ClassName.bestGuess("org.springframework.web.bind.annotation.PostMapping"); - static final ClassName HttpServletRequest = ClassName.bestGuess("javax.servlet.http.HttpServletRequest"); - static final ClassName HttpServletResponse = ClassName.bestGuess("javax.servlet.http.HttpServletResponse"); + static final ClassName HttpServletRequest = ClassName.bestGuess("jakarta.servlet.http.HttpServletRequest"); + static final ClassName HttpServletResponse = ClassName.bestGuess("jakarta.servlet.http.HttpServletResponse"); private final String context; private final TypeSpec.Builder rpcController; diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt index 4af39fd..d7130fe 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt @@ -9,11 +9,11 @@ public interface RpcHelloWorld { [1] = package com.example.helloworld; import com.google.protobuf.util.JsonFormat; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.InputStreamReader; import java.lang.Exception; import java.nio.charset.StandardCharsets; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt index 8922a4c..1c488ad 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -1,6 +1,6 @@ [0] = package com.example.helloworld; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; public interface RpcHelloWorld { Helloworld.HelloResp handleHello(HttpServletRequest request, Helloworld.HelloReq in); @@ -11,11 +11,11 @@ public interface RpcHelloWorld { [1] = package com.example.helloworld; import com.google.protobuf.util.JsonFormat; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.InputStreamReader; import java.lang.Exception; import java.nio.charset.StandardCharsets; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt index c5d6a51..1a58b3b 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt @@ -7,11 +7,11 @@ public interface RpcStatus { [1] = package com.example.helloworld; import com.google.protobuf.util.JsonFormat; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.InputStreamReader; import java.lang.Exception; import java.nio.charset.StandardCharsets; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/runtime/core/build.gradle b/runtime/core/build.gradle index 06ad5ec..d9f4b55 100644 --- a/runtime/core/build.gradle +++ b/runtime/core/build.gradle @@ -7,7 +7,7 @@ publishing { publications { maven(MavenPublication) { artifactId = 'flit-core-runtime' - version = "1.3" + version = "2.0" from components.java } diff --git a/runtime/jakarta/build.gradle b/runtime/jakarta/build.gradle index 080ee66..c82aeb9 100644 --- a/runtime/jakarta/build.gradle +++ b/runtime/jakarta/build.gradle @@ -12,7 +12,7 @@ publishing { publications { maven(MavenPublication) { artifactId = 'flit-jakarta-runtime' - version = "1.3" + version = "2.0" from components.java } diff --git a/runtime/jaxrs/build.gradle b/runtime/jaxrs/build.gradle index 732ac47..7aa4d82 100644 --- a/runtime/jaxrs/build.gradle +++ b/runtime/jaxrs/build.gradle @@ -12,7 +12,7 @@ publishing { publications { maven(MavenPublication) { artifactId = 'flit-jaxrs-runtime' - version = "1.3" + version = "2.0" from components.java } diff --git a/runtime/spring/build.gradle b/runtime/spring/build.gradle index 8f3296a..d9af59a 100644 --- a/runtime/spring/build.gradle +++ b/runtime/spring/build.gradle @@ -1,7 +1,7 @@ dependencies { api(project(':runtime:core')) compileOnly("org.slf4j:slf4j-api:$slf4jVersion") - compileOnly("javax.servlet:javax.servlet-api:$javaxservletapiVersion") + compileOnly("jakarta.servlet:jakarta.servlet-api:$jakartaservletapiVersion") compileOnly("org.springframework:spring-webmvc:$springVersion") } @@ -9,7 +9,7 @@ publishing { publications { maven(MavenPublication) { artifactId = 'flit-spring-runtime' - version = "1.3" + version = "2.0" from components.java } diff --git a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java index bf9e607..494a820 100644 --- a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java +++ b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitExceptionHandler.java @@ -3,6 +3,9 @@ import com.flit.runtime.ErrorCode; import com.flit.runtime.FlitException; import com.google.protobuf.InvalidProtocolBufferException; +import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -12,17 +15,12 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; - @ControllerAdvice @Component public class FlitExceptionHandler extends ResponseEntityExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(FlitExceptionHandler.class); - @ExceptionHandler(Exception.class) public ResponseEntity handleException(HttpServletRequest request, Exception e) { LOGGER.error( @@ -42,7 +40,7 @@ public ResponseEntity handleException(HttpServletRequest request, Exception e } @ExceptionHandler(InvalidProtocolBufferException.class) - public ResponseEntity handlehandleInvalidProtocolBufferException(HttpServletRequest request, Exception e) { + public ResponseEntity handleInvalidProtocolBufferException(HttpServletRequest request, Exception e) { LOGGER.error("InvalidProtocolBufferException: request = {}, method = {}, msg= {}", request.getRequestURI(), request.getMethod(), e.getMessage(), e); diff --git a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitHandlerExceptionResolver.java b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitHandlerExceptionResolver.java index 0cf1635..1a1bf2c 100644 --- a/runtime/spring/src/main/java/com/flit/runtime/spring/FlitHandlerExceptionResolver.java +++ b/runtime/spring/src/main/java/com/flit/runtime/spring/FlitHandlerExceptionResolver.java @@ -1,26 +1,26 @@ package com.flit.runtime.spring; import com.flit.runtime.ErrorCode; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; +import org.springframework.lang.Nullable; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.View; import org.springframework.web.servlet.view.json.MappingJackson2JsonView; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - public class FlitHandlerExceptionResolver implements HandlerExceptionResolver, Ordered { private static final Logger LOGGER = LoggerFactory.getLogger(FlitHandlerExceptionResolver.class); - private View view = new MappingJackson2JsonView(); + private final View view = new MappingJackson2JsonView(); @Override - public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { LOGGER.warn("Handling internal exception: error = {}", ex.getMessage()); diff --git a/runtime/spring/src/main/java/com/flit/runtime/spring/package-info.java b/runtime/spring/src/main/java/com/flit/runtime/spring/package-info.java new file mode 100644 index 0000000..19335ad --- /dev/null +++ b/runtime/spring/src/main/java/com/flit/runtime/spring/package-info.java @@ -0,0 +1,6 @@ +@NonNullApi +@NonNullFields +package com.flit.runtime.spring; + +import org.springframework.lang.NonNullApi; +import org.springframework.lang.NonNullFields; \ No newline at end of file diff --git a/runtime/spring/src/main/resources/application.properties b/runtime/spring/src/main/resources/application.properties deleted file mode 100644 index a2ae767..0000000 --- a/runtime/spring/src/main/resources/application.properties +++ /dev/null @@ -1,3 +0,0 @@ -spring.mvc.throw-exception-if-no-handler-found=true -spring.resources.add-mappings=false -server.error.whitelabel.enabled=false \ No newline at end of file diff --git a/runtime/spring/src/main/resources/application.yaml b/runtime/spring/src/main/resources/application.yaml new file mode 100644 index 0000000..9e19a3c --- /dev/null +++ b/runtime/spring/src/main/resources/application.yaml @@ -0,0 +1,3 @@ +spring.mvc.throw-exception-if-no-handler-found: true +spring.resources.add-mappings: false +server.error.whitelabel.enabled: false diff --git a/runtime/undertow/build.gradle b/runtime/undertow/build.gradle index a5f9f6a..aedf51d 100644 --- a/runtime/undertow/build.gradle +++ b/runtime/undertow/build.gradle @@ -8,7 +8,7 @@ publishing { publications { maven(MavenPublication) { artifactId = 'flit-undertow-runtime' - version = "1.3" + version = "2.0" from components.java } From 9ba16c4175a333371608827bc9450d4998b1eb9c Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Thu, 30 May 2024 11:49:00 -0700 Subject: [PATCH 28/44] update gradle version to 8.7 (#15) --- gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 12 ++++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 21856 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 delta 20086 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/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -197,6 +197,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in From d8d5cacb87e547f9ed47009a6d342952c1c213e4 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Fri, 21 Jun 2024 12:08:05 -0700 Subject: [PATCH 29/44] upgrade gradle to 8.8, add depbot config and wrapper update workflow (#16) --- .github/dependabot.yaml | 15 +++++++++++++++ .github/workflows/gradle-upgrade.yaml | 19 +++++++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 19 ++++++++++--------- gradlew.bat | 20 ++++++++++---------- 6 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 .github/dependabot.yaml create mode 100644 .github/workflows/gradle-upgrade.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..73268a6 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,15 @@ +--- +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + ignore: + # private repo has trouble with access, plus we're using main anyways + - dependency-name: github/data-pipelines + reviewers: + - github/data-pipelines + groups: + actions: + patterns: ['*'] diff --git a/.github/workflows/gradle-upgrade.yaml b/.github/workflows/gradle-upgrade.yaml new file mode 100644 index 0000000..22832ca --- /dev/null +++ b/.github/workflows/gradle-upgrade.yaml @@ -0,0 +1,19 @@ +name: Upgrade Gradle Wrapper + +on: + workflow_dispatch: + schedule: + # monday at midnight + - cron: '0 0 * * 1' + +permissions: + contents: write + pull-requests: write + +jobs: + upgrade-wrapper: + uses: github/data-pipelines/.github/workflows/gradle-upgrade.yaml@main + secrets: inherit + with: + java-version: 17 + base-branch: master diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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

NnX)EH+Nua)3Y(c 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≫L+crb^Bz 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 /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 6689b85..7101f8e 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From 1a38b32abee5575824624774242a7597262cfe9a Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Fri, 21 Jun 2024 12:13:34 -0700 Subject: [PATCH 30/44] remove workflow because of private access (#17) --- .github/workflows/gradle-upgrade.yaml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/gradle-upgrade.yaml diff --git a/.github/workflows/gradle-upgrade.yaml b/.github/workflows/gradle-upgrade.yaml deleted file mode 100644 index 22832ca..0000000 --- a/.github/workflows/gradle-upgrade.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Upgrade Gradle Wrapper - -on: - workflow_dispatch: - schedule: - # monday at midnight - - cron: '0 0 * * 1' - -permissions: - contents: write - pull-requests: write - -jobs: - upgrade-wrapper: - uses: github/data-pipelines/.github/workflows/gradle-upgrade.yaml@main - secrets: inherit - with: - java-version: 17 - base-branch: master From b0df20e2eeccf3bcf6acc251daa88aa82aed4389 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:18:49 -0700 Subject: [PATCH 31/44] Bump the actions group with 3 updates (#18) Bumps the actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/setup-java](https://github.com/actions/setup-java) and [github/codeql-action](https://github.com/github/codeql-action). Updates `actions/checkout` from 3 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) Updates `actions/setup-java` from 3 to 4 - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) Updates `github/codeql-action` from 2 to 3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 4 ++-- .github/workflows/codeql-analysis.yml | 10 +++++----- .github/workflows/publish.yaml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4723b46..3394ef5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,9 +16,9 @@ jobs: contents: read packages: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'zulu' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8d626a1..7a4df9f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,17 +39,17 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'zulu' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -60,7 +60,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} @@ -77,4 +77,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index af4fa53..4f88f2b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -12,8 +12,8 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: java-version: '17' distribution: 'zulu' From b64feb44423803ab5d4b771ded1790e622a34602 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Mon, 28 Jul 2025 14:11:55 -0700 Subject: [PATCH 32/44] add try-with-resources around json parsing in controllers (#19) --- build.gradle | 3 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- plugin/build.gradle | 2 +- .../com/flit/protoc/gen/server/jakarta/RpcGenerator.java | 8 ++++++-- .../com/flit/protoc/gen/server/jaxrs/RpcGenerator.java | 8 ++++++-- .../com/flit/protoc/gen/server/spring/RpcGenerator.java | 8 ++++++-- .../flit/protoc/gen/server/undertow/RpcGenerator.java | 9 ++++++++- .../HelloworldGeneratorTest.test_Generate.approved.txt | 8 ++++++-- ...ldGeneratorTest.test_GenerateWithRequest.approved.txt | 8 ++++++-- .../StatusGeneratorTest.test_Generate.approved.txt | 4 +++- .../HelloworldGeneratorTest.test_Generate.approved.txt | 8 ++++++-- ...ldGeneratorTest.test_GenerateWithRequest.approved.txt | 8 ++++++-- .../jaxrs/StatusGeneratorTest.test_Generate.approved.txt | 4 +++- .../HelloworldGeneratorTest.test_Generate.approved.txt | 8 ++++++-- ...ldGeneratorTest.test_GenerateWithRequest.approved.txt | 8 ++++++-- .../StatusGeneratorTest.test_Generate.approved.txt | 4 +++- .../HelloworldGeneratorTest.test_Generate.approved.txt | 8 ++++++-- ...ldGeneratorTest.test_GenerateWithRequest.approved.txt | 8 ++++++-- .../StatusGeneratorTest.test_Generate.approved.txt | 4 +++- 19 files changed, 90 insertions(+), 30 deletions(-) diff --git a/build.gradle b/build.gradle index 77517fd..aa335f9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,13 @@ plugins { id 'java-library' id 'maven-publish' - id 'dev.poolside.gradle.semantic-version' version '0.1.5' + id 'dev.poolside.gradle.semantic-version' version '1.0.0' } allprojects { apply plugin: 'java-library' apply plugin: 'maven-publish' + apply plugin: 'dev.poolside.gradle.semantic-version' repositories { mavenCentral() diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a441313..d4081da 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/plugin/build.gradle b/plugin/build.gradle index cd54ba7..63caba9 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.github.johnrengelman.shadow" version "8.1.1" + id 'com.gradleup.shadow' version '8.3.8' } dependencies { diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java index bc26e48..3fbcf2a 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jakarta/RpcGenerator.java @@ -78,10 +78,14 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .nextControlFlow("else if (request.getContentType().startsWith($S))", "application/json") .addStatement("json = true") .addStatement("$T.Builder builder = $T.newBuilder()", inputType, inputType) - .addStatement("$T.parser().merge(new $T(request.getInputStream(), $T.UTF_8), builder)", - Types.JsonFormat, + // try with resources + .beginControlFlow("try ($T reader = new $T(request.getInputStream(), $T.UTF_8))", + Types.InputStreamReader, Types.InputStreamReader, Types.StandardCharsets) + .addStatement("$T.parser().merge(reader, builder)", + Types.JsonFormat) + .endControlFlow() .addStatement("data = builder.build()") .nextControlFlow("else") .addStatement("response.setStatus(415)") diff --git a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java index 8e1f3c4..bd14e0b 100644 --- a/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java +++ b/plugin/src/main/java/com/flit/protoc/gen/server/jaxrs/RpcGenerator.java @@ -78,10 +78,14 @@ private void addHandleMethod(MethodDescriptorProto mdp) { .nextControlFlow("else if (request.getContentType().startsWith($S))", "application/json") .addStatement("json = true") .addStatement("$T.Builder builder = $T.newBuilder()", inputType, inputType) - .addStatement("$T.parser().merge(new $T(request.getInputStream(), $T.UTF_8), builder)", - Types.JsonFormat, + // try with resources + .beginControlFlow("try ($T reader = new $T(request.getInputStream(), $T.UTF_8))", + Types.InputStreamReader, Types.InputStreamReader, Types.StandardCharsets) + .addStatement("$T.parser().merge(reader, builder)", + Types.JsonFormat) + .endControlFlow() .addStatement("data = builder.build()") .nextControlFlow("else") .addStatement("response.setStatus(415)") 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 facfe76..44e1595 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 @@ -61,10 +61,14 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .nextControlFlow("else if (request.getContentType().startsWith($S))", "application/json") .addStatement("json = true") .addStatement("$T.Builder builder = $T.newBuilder()", inputType, inputType) - .addStatement("$T.parser().merge(new $T(request.getInputStream(), $T.UTF_8), builder)", - Types.JsonFormat, + // try with resources + .beginControlFlow("try ($T reader = new $T(request.getInputStream(), $T.UTF_8))", + Types.InputStreamReader, Types.InputStreamReader, Types.StandardCharsets) + .addStatement("$T.parser().merge(reader, builder)", + Types.JsonFormat) + .endControlFlow() .addStatement("data = builder.build()") .nextControlFlow("else") .addStatement("response.setStatus(415)") 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 5ec23ba..160ac88 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 @@ -137,7 +137,14 @@ private void writeHandleMethod(DescriptorProtos.MethodDescriptorProto m) { .nextControlFlow("else if (contentType.startsWith($S))", "application/json") .addStatement("json = true") .addStatement("$T.Builder builder = $T.newBuilder()", inputType, inputType) - .addStatement("$T.parser().merge(new $T(exchange.getInputStream(), $T.UTF_8), builder)", JsonFormat, InputStreamReader, StandardCharsets) + // try with resources + .beginControlFlow("try ($T reader = new $T(exchange.getInputStream(), $T.UTF_8))", + InputStreamReader, + InputStreamReader, + StandardCharsets) + .addStatement("$T.parser().merge(reader, builder)", + JsonFormat) + .endControlFlow() .addStatement("data = builder.build()") .nextControlFlow("else") .addStatement("exchange.setStatusCode(415)") diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt index 21cd4d1..9393d6f 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_Generate.approved.txt @@ -37,7 +37,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); @@ -67,7 +69,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt index b97fe8e..a787e36 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -39,7 +39,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); @@ -69,7 +71,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt index 41d4639..65361e6 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jakarta/StatusGeneratorTest.test_Generate.approved.txt @@ -35,7 +35,9 @@ public class RpcStatusResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Core.Empty.Builder builder = Core.Empty.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt index a76f26e..d39fd52 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_Generate.approved.txt @@ -37,7 +37,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); @@ -67,7 +69,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt index 4dbc450..d882f8c 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -39,7 +39,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); @@ -69,7 +71,9 @@ public class RpcHelloWorldResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt index a466727..8b5157d 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/jaxrs/StatusGeneratorTest.test_Generate.approved.txt @@ -35,7 +35,9 @@ public class RpcStatusResource { } else if (request.getContentType().startsWith("application/json")) { json = true; Core.Empty.Builder builder = Core.Empty.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt index d7130fe..ee260ea 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_Generate.approved.txt @@ -33,7 +33,9 @@ public class RpcHelloWorldController { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); @@ -60,7 +62,9 @@ public class RpcHelloWorldController { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt index 1c488ad..3589ff7 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -35,7 +35,9 @@ public class RpcHelloWorldController { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); @@ -62,7 +64,9 @@ public class RpcHelloWorldController { } else if (request.getContentType().startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt index 1a58b3b..db9111e 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/spring/StatusGeneratorTest.test_Generate.approved.txt @@ -31,7 +31,9 @@ public class RpcStatusController { } else if (request.getContentType().startsWith("application/json")) { json = true; Core.Empty.Builder builder = Core.Empty.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { response.setStatus(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt index 51361d0..0db3aa3 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_Generate.approved.txt @@ -69,7 +69,9 @@ public class RpcHelloWorldHandler implements HttpHandler { } else if (contentType.startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { exchange.setStatusCode(415); @@ -95,7 +97,9 @@ public class RpcHelloWorldHandler implements HttpHandler { } else if (contentType.startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { exchange.setStatusCode(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt index ec2a09e..760a38e 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/HelloworldGeneratorTest.test_GenerateWithRequest.approved.txt @@ -71,7 +71,9 @@ public class RpcHelloWorldHandler implements HttpHandler { } else if (contentType.startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { exchange.setStatusCode(415); @@ -97,7 +99,9 @@ public class RpcHelloWorldHandler implements HttpHandler { } else if (contentType.startsWith("application/json")) { json = true; Helloworld.HelloReq.Builder builder = Helloworld.HelloReq.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { exchange.setStatusCode(415); diff --git a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt index 884a706..2784862 100644 --- a/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt +++ b/plugin/src/test/java/com/flit/protoc/gen/server/undertow/StatusGeneratorTest.test_Generate.approved.txt @@ -66,7 +66,9 @@ public class RpcStatusHandler implements HttpHandler { } else if (contentType.startsWith("application/json")) { json = true; Core.Empty.Builder builder = Core.Empty.newBuilder(); - JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8), builder); + try (InputStreamReader reader = new InputStreamReader(exchange.getInputStream(), StandardCharsets.UTF_8)) { + JsonFormat.parser().merge(reader, builder); + } data = builder.build(); } else { exchange.setStatusCode(415); From 8b24f934d03a36aa056db6298d29ea70f6ebf9a9 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Mon, 11 Aug 2025 11:54:13 -0700 Subject: [PATCH 33/44] update third party libraries (#21) --- build.gradle | 20 ++++++++++---------- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 10 ++++++---- gradlew.bat | 6 ++++-- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index aa335f9..df98bbc 100644 --- a/build.gradle +++ b/build.gradle @@ -20,18 +20,18 @@ allprojects { javapoetVersion = "1.13.0" javaxservletapiVersion = "4.0.1" javaxwsrsapiVersion = "2.1.1" - protobufVersion = "3.23.4" - slf4jVersion = "1.7.36" - springVersion = "6.0.11" - undertowVersion = "2.3.5.Final" + protobufVersion = "3.25.5" + slf4jVersion = "2.0.17" + springVersion = "6.2.8" + undertowVersion = "2.3.18.Final" // testing - approvaltestsVersion = "18.7.1" - javaparserVersion = "3.25.4" - jerseyCommonJavaxVersion = "2.22.2" - jerseyCommonJakartaVersion = "3.1.3" - junitJupiterVersion = "5.10.0" - mockitoVersion = "5.4.0" + approvaltestsVersion = "24.22.0" + javaparserVersion = "3.27.0" + jerseyCommonJavaxVersion = "2.47" + jerseyCommonJakartaVersion = "3.1.10" + junitJupiterVersion = "5.12.2" + mockitoVersion = "5.18.0" } java { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 35073 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 delta 34912 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

Gn4F+B~IivciU9^t0j-Mgao3 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 dDuazZG^u*-) zCV{{p$D*4m=Z;!QJ0C3EBfaUv#@3CmRgS-#a4+Uk9LtR(mzdSt-?*B-Q{Jgs!F;+_ zT&Jc~{N951OskjVtnWOLnfUacmSEkQ#fQojqa=HN?aN#ubFA){K&TrfoSnBphJ<43S zS5?OUrqPYpEqf;(zc;IIty0d`-n-Ke%PlArJt^DN>ZYW)ZS5z$Rnx=cxbM1#zPRNS1N0u>c z*gpBeTvyKAjdr#Uz%$J3C+p2~l9`-P6`=_1Y83#nAp`Kd5jfb*H#u;=%w#=&g~|Q% z1!N9S7n+#|tj#t98*^$X3S9)i3I*n?N+YghXJ9Z!QN%wvV7|W$;;Lx|1{)Ol%c4L- zlz|txtAK4ozLL`xMPZR7Sm6grRj@+Db&BXF9#sG|gMywm=IO1O01imHv3 zFjX9ri>su;HX}wK85k5$R0A&?fdtA5_Q_vX$jc(elo%M~QB>A-K~#2igOjH$d^80Z zC8#NjzZarXrcVZJJEF73z+ix4=7c`5N>TJt7>~rfl$^E}UAMlP2$P)vS26{cE?ONf_&Vd@-E2?PMU+S~pB diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da..2a84e18 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index b740cf1..23d15a9 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -84,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -203,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -211,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 7101f8e..5eed7ee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From 5f86dfc533316bb4ab220c62c7a4d2ace8581830 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Fri, 15 Aug 2025 11:37:40 -0700 Subject: [PATCH 34/44] force commons-lang3 version to 3.18.0 (#23) --- build.gradle | 4 ++++ plugin/build.gradle | 1 + 2 files changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index df98bbc..aaf7a4d 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,10 @@ allprojects { jerseyCommonJakartaVersion = "3.1.10" junitJupiterVersion = "5.12.2" mockitoVersion = "5.18.0" + + // force version for security + // remove when upgrading other libraries + commonsLang3Version = "3.18.0" // caused by approvaltestsVersion } java { diff --git a/plugin/build.gradle b/plugin/build.gradle index 63caba9..f63d143 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -9,6 +9,7 @@ dependencies { testImplementation("com.github.javaparser:javaparser-core:$javaparserVersion") testImplementation("com.approvaltests:approvaltests:$approvaltestsVersion") + testImplementation("org.apache.commons:commons-lang3:$commonsLang3Version") } shadowJar { From 21880871286ebb6869218e6be3387de86ea187c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:29:05 -0700 Subject: [PATCH 35/44] Bump actions/checkout from 4 to 5 in the actions group (#24) Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4 to 5 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/publish.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3394ef5..719f8c0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,7 +16,7 @@ jobs: contents: read packages: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK 17 uses: actions/setup-java@v4 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7a4df9f..b216428 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 uses: actions/setup-java@v4 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 4f88f2b..296310d 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -12,7 +12,7 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: java-version: '17' From a277ddd77bc36c31d03a3595b7519a0a20890380 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Aug 2025 10:23:24 -0700 Subject: [PATCH 36/44] Bump actions/setup-java from 4 to 5 in the actions group (#25) Bumps the actions group with 1 update: [actions/setup-java](https://github.com/actions/setup-java). Updates `actions/setup-java` from 4 to 5 - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/publish.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 719f8c0..b83c0f5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'zulu' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b216428..a569d52 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'zulu' diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 296310d..2d7292c 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -13,7 +13,7 @@ jobs: packages: write steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'zulu' From 6530d5862f1825728bf787fa3a6828e185f69056 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Thu, 4 Sep 2025 15:58:13 -0700 Subject: [PATCH 37/44] Add 'actions' to CodeQL analysis language matrix (#27) --- .github/workflows/codeql-analysis.yml | 34 +++++++-------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a569d52..28a0812 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,9 +33,11 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'java' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://git.io/codeql-language-support + include: + - language: actions + build-mode: none + - language: java + build-mode: none steps: - name: Checkout repository @@ -52,29 +54,9 @@ jobs: uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release + build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" From de0ca63ec226b7d663c1c8ec7a312cdc9005f2e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 10:52:51 -0700 Subject: [PATCH 38/44] Bump org.springframework:spring-webmvc (#26) Bumps the gradle group with 1 update in the / directory: [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework). Updates `org.springframework:spring-webmvc` from 6.2.8 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.10) --- updated-dependencies: - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.10 dependency-type: direct:production dependency-group: gradle ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index aaf7a4d..8a0819f 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ allprojects { javaxwsrsapiVersion = "2.1.1" protobufVersion = "3.25.5" slf4jVersion = "2.0.17" - springVersion = "6.2.8" + springVersion = "6.2.10" undertowVersion = "2.3.18.Final" // testing From 906ae834c4400237590ae9c98b0f56e6aa4f82b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:26:21 -0700 Subject: [PATCH 39/44] Bump github/codeql-action from 3 to 4 in the actions group (#28) Bumps the actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3 to 4 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 28a0812..d050679 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,12 +51,12 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" From 3639b91a2c041f75e608f736cdf06613fca38ca5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:54:39 -0700 Subject: [PATCH 40/44] Bump io.undertow:undertow-core in the gradle group across 1 directory (#29) Bumps the gradle group with 1 update in the / directory: [io.undertow:undertow-core](https://github.com/undertow-io/undertow). Updates `io.undertow:undertow-core` from 2.3.18.Final to 2.3.19.Final - [Release notes](https://github.com/undertow-io/undertow/releases) - [Commits](https://github.com/undertow-io/undertow/compare/2.3.18.Final...2.3.19.Final) --- updated-dependencies: - dependency-name: io.undertow:undertow-core dependency-version: 2.3.19.Final dependency-type: direct:production dependency-group: gradle ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8a0819f..e8f062a 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ allprojects { protobufVersion = "3.25.5" slf4jVersion = "2.0.17" springVersion = "6.2.10" - undertowVersion = "2.3.18.Final" + undertowVersion = "2.3.19.Final" // testing approvaltestsVersion = "24.22.0" From 6a4948b4b37c9834c1677057b91ccfd2a214995e Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Mon, 20 Oct 2025 15:35:20 -0700 Subject: [PATCH 41/44] spring 6.2.11 (#30) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e8f062a..5ed5e55 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ allprojects { javaxwsrsapiVersion = "2.1.1" protobufVersion = "3.25.5" slf4jVersion = "2.0.17" - springVersion = "6.2.10" + springVersion = "6.2.11" undertowVersion = "2.3.19.Final" // testing From 9a2fcc7276083ffde5ba5a050e6b232542994ac1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:46:16 -0800 Subject: [PATCH 42/44] Bump actions/checkout from 5 to 6 in the actions group (#31) Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 5 to 6 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/publish.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b83c0f5..d0859b6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,7 +16,7 @@ jobs: contents: read packages: read steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 17 uses: actions/setup-java@v5 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d050679..85ac0b5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up JDK 17 uses: actions/setup-java@v5 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 2d7292c..113e22a 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -12,7 +12,7 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: java-version: '17' From 8c522d5281b8fff70a1f4212c1f707fb1c0feb60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:50:28 -0800 Subject: [PATCH 43/44] Bump io.undertow:undertow-core in the gradle group across 1 directory (#32) Bumps the gradle group with 1 update in the / directory: [io.undertow:undertow-core](https://github.com/undertow-io/undertow). Updates `io.undertow:undertow-core` from 2.3.19.Final to 2.3.20.Final - [Release notes](https://github.com/undertow-io/undertow/releases) - [Commits](https://github.com/undertow-io/undertow/compare/2.3.19.Final...2.3.20.Final) --- updated-dependencies: - dependency-name: io.undertow:undertow-core dependency-version: 2.3.20.Final dependency-type: direct:production dependency-group: gradle ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5ed5e55..43e41f9 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ allprojects { protobufVersion = "3.25.5" slf4jVersion = "2.0.17" springVersion = "6.2.11" - undertowVersion = "2.3.19.Final" + undertowVersion = "2.3.20.Final" // testing approvaltestsVersion = "24.22.0" From 3240092a6df09905e483560388197b45e563f325 Mon Sep 17 00:00:00 2001 From: Rachel Aurand Date: Wed, 7 Jan 2026 14:26:18 -0800 Subject: [PATCH 44/44] shadow 9.3.1 (#33) --- .github/CODEOWNERS | 1 + plugin/build.gradle | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..d8b54d5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @github/data-pipelines diff --git a/plugin/build.gradle b/plugin/build.gradle index f63d143..c379372 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.gradleup.shadow' version '8.3.8' + id 'com.gradleup.shadow' version '9.3.1' } dependencies { @@ -22,10 +22,10 @@ shadowJar { publishing { publications { - shadow(MavenPublication) { publication -> + shadow(MavenPublication) { artifactId = 'flit-plugin' version = '2.0' - project.shadow.component(publication) + from components.shadow } } }