diff --git a/pom.xml b/pom.xml index c3bbeb4..c0f35cf 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ 3.0.0 2.0.15.Final 2.9.6 - 2.0.6-SNAPSHOT + 2.0.6 1.5.21 diff --git a/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java b/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java index e555cd9..132b11c 100644 --- a/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java +++ b/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java @@ -12,6 +12,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -55,9 +56,10 @@ import io.sinistral.proteus.server.Extractors; import io.sinistral.proteus.server.ServerRequest; import io.sinistral.proteus.server.ServerResponse; -import io.sinistral.proteus.server.endpoints.EndpointInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.sinistral.proteus.server.endpoints.EndpointInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tags; import io.undertow.server.HandlerWrapper; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; @@ -100,6 +102,16 @@ public enum StatementParameterType protected Set registeredEndpoints; protected Class controllerClass; + + protected Set injectedHandlerWrappers = new HashSet<>(); + + @Inject + @Named("registeredHandlerWrappers") + protected Map registeredHandlerWrappers; + + protected Map, String> typeLevelHandlerWrapperMap = new LinkedHashMap, String>(); + + protected Map, String> handlerWrapperMap = new LinkedHashMap, String>(); /** * Create a new {@code HandlerGenerator} instance used to generate a @@ -144,6 +156,51 @@ protected void generateRoutes() { try { + +// Optional typeLevelWrapAnnotation = Optional.ofNullable(controllerClass.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); +// +// typeLevelWrapAnnotation.ifPresent( a -> { +// +// io.sinistral.proteus.annotations.Chain w = typeLevelWrapAnnotation.get(); +// +// Class wrapperClasses[] = w.value(); +// +// for (int i = 0; i < wrapperClasses.length; i++) +// { +// Class wrapperClass = wrapperClasses[i]; +// +// String wrapperName = generateFieldName(wrapperClass.getCanonicalName()); +// +// handlerWrapperMap.put(wrapperClass, wrapperName); +// } +// +// }); +// +// for(Method m : this.controllerClass.getDeclaredMethods()) +// { +// +// Optional methodLevelWrapAnnotation = Optional.ofNullable(m.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); +// +// methodLevelWrapAnnotation.ifPresent( a -> { +// +// io.sinistral.proteus.annotations.Chain w = methodLevelWrapAnnotation.get(); +// +// Class wrapperClasses[] = w.value(); +// +// for (int i = 0; i < wrapperClasses.length; i++) +// { +// Class wrapperClass = wrapperClasses[i]; +// +// String wrapperName = generateFieldName(wrapperClass.getCanonicalName()); +// +// handlerWrapperMap.put(wrapperClass, wrapperName); +// } +// +// }); +// +// } +// +// log.info("handlerWrapperMap: " + handlerWrapperMap); TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC) .addSuperinterface(ParameterizedTypeName.get(Supplier.class, RoutingHandler.class)); @@ -151,6 +208,8 @@ protected void generateRoutes() ClassName extractorClass = ClassName.get("io.sinistral.proteus.server", "Extractors"); ClassName injectClass = ClassName.get("com.google.inject", "Inject"); + + MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addAnnotation(injectClass); @@ -168,9 +227,27 @@ protected void generateRoutes() .annotated(AnnotationSpec.builder(com.google.inject.name.Named.class).addMember("value", "$S", "registeredHandlerWrappers").build()); typeBuilder.addField(mapOfWrappers, "registeredHandlerWrappers", Modifier.PROTECTED, Modifier.FINAL); + +// registeredHandlerWrappers.keySet().stream().forEach( k -> { +// +// typeBuilder.addField(HandlerWrapper.class, k + "HandlerWrapper", Modifier.PROTECTED, Modifier.FINAL); +// +// }); constructor.addParameter(this.controllerClass, className); constructor.addParameter(annotatedMapOfWrappers, "registeredHandlerWrappers"); + +// registeredHandlerWrappers.keySet().stream().forEach( k -> { +// +// ParameterSpec p = ParameterSpec.builder(HandlerWrapper.class, k + "HandlerWrapper").addAnnotation(AnnotationSpec.builder(com.google.inject.name.Named.class).addMember("value", "$S", k).build()).build(); +// +// constructor.addParameter(p); +// +// }); + + // String.format("%c%s%sHandler_%s", Character.toLowerCase(clazz.getSimpleName().charAt(0)), clazz.getSimpleName() + //.substring(1, clazz.getSimpleName().length()), StringUtils.capitalize(m.getName()), String.valueOf(nameIndex++)); + constructor.addStatement("this.$N = $N", className, className); constructor.addStatement("this.$N = $N", "registeredHandlerWrappers", "registeredHandlerWrappers"); @@ -206,7 +283,7 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class cla .addStatement("final $T router = new $T()", io.undertow.server.RoutingHandler.class, io.undertow.server.RoutingHandler.class); final Map parameterizedLiteralsNameMap = Arrays.stream(clazz.getDeclaredMethods()) - .filter(m -> m.getAnnotation(ApiOperation.class) != null) + .filter(m -> m.getAnnotation(Operation.class) != null) .flatMap( m -> Arrays.stream(m.getParameters()).map(Parameter::getParameterizedType) .filter(t -> t.getTypeName().contains("<") && !t.getTypeName().contains("concurrent"))) @@ -219,7 +296,7 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class cla }).collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForParameterizedType)); Arrays.stream(clazz.getDeclaredMethods()) - .filter(m -> m.getAnnotation(ApiOperation.class) != null) + .filter(m -> m.getAnnotation(Operation.class) != null) .flatMap(m -> Arrays.stream(m.getParameters())) .forEach(p -> { @@ -242,7 +319,7 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class cla }); final Map literalsNameMap = Arrays.stream(clazz.getDeclaredMethods()) - .filter(m -> m.getAnnotation(ApiOperation.class) != null) + .filter(m -> m.getAnnotation(Operation.class) != null) .flatMap(m -> Arrays.stream(m.getParameters()) .map(Parameter::getParameterizedType)).filter(t -> { @@ -321,8 +398,7 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) Optional typeLevelWrapAnnotation = Optional.ofNullable(clazz.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); - Map, String> typeLevelHandlerWrapperMap = new LinkedHashMap, String>(); - + if (typeLevelWrapAnnotation.isPresent()) { io.sinistral.proteus.annotations.Chain w = typeLevelWrapAnnotation.get(); @@ -354,21 +430,15 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) List typeLevelSecurityDefinitions = new ArrayList<>(); - if (Optional.ofNullable(clazz.getAnnotation(io.swagger.annotations.Api.class)).isPresent()) + if (Optional.ofNullable(clazz.getAnnotation(Tags.class)).isPresent()) { - io.swagger.annotations.Api apiAnnotation = clazz.getAnnotation(io.swagger.annotations.Api.class); - - io.swagger.annotations.Authorization[] authorizationAnnotations = apiAnnotation.authorizations(); + SecurityRequirement securityRequirementAnnotation = clazz.getAnnotation(SecurityRequirement.class); - if (authorizationAnnotations.length > 0) + if(securityRequirementAnnotation != null) { - for (io.swagger.annotations.Authorization authorizationAnnotation : authorizationAnnotations) - { - if (authorizationAnnotation.value().length() > 0) - { - typeLevelSecurityDefinitions.add(authorizationAnnotation.value()); - } - } + String securityRequirement = securityRequirementAnnotation.name(); + + typeLevelSecurityDefinitions.add(securityRequirement); } } @@ -812,22 +882,17 @@ else if (producesContentType.contains(MediaType.TEXT_HTML)) * @TODO wrap blocking in BlockingHandler */ - if (Optional.ofNullable(m.getAnnotation(io.swagger.annotations.ApiOperation.class)).isPresent()) + if (Optional.ofNullable(m.getAnnotation(Operation.class)).isPresent()) { - io.swagger.annotations.ApiOperation apiOperationAnnotation = m.getAnnotation(io.swagger.annotations.ApiOperation.class); + SecurityRequirement securityRequirementAnnotation = m.getAnnotation(SecurityRequirement.class); - io.swagger.annotations.Authorization[] authorizationAnnotations = apiOperationAnnotation.authorizations(); - - if (authorizationAnnotations.length > 0) + if(securityRequirementAnnotation != null) { - for (io.swagger.annotations.Authorization authorizationAnnotation : authorizationAnnotations) - { - if (authorizationAnnotation.value().length() > 0) - { - securityDefinitions.add(authorizationAnnotation.value()); - } - } + String securityRequirement = securityRequirementAnnotation.name(); + + securityDefinitions.add(securityRequirement); } + } if (securityDefinitions.isEmpty()) @@ -977,7 +1042,7 @@ protected static Set> getApiClasses(String basePath, Predicate { Reflections ref = new Reflections(basePath); - Stream> stream = ref.getTypesAnnotatedWith(Api.class).stream(); + Stream> stream = ref.getTypesAnnotatedWith(Path.class).stream(); if (pathPredicate != null) { diff --git a/src/test/java/io/sinistral/proteus/test/controllers/Tests.java b/src/test/java/io/sinistral/proteus/test/controllers/Tests.java index 385d920..26963be 100644 --- a/src/test/java/io/sinistral/proteus/test/controllers/Tests.java +++ b/src/test/java/io/sinistral/proteus/test/controllers/Tests.java @@ -41,6 +41,8 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.tags.Tags; import io.undertow.server.HttpServerExchange; /** @@ -48,6 +50,7 @@ * */ @Api(tags="tests") +@Tags({@Tag(name = "tests")}) @Path("/tests") @Produces((MediaType.APPLICATION_JSON)) @Consumes((MediaType.MEDIA_TYPE_WILDCARD)) @@ -69,7 +72,7 @@ public class Tests @GET @Path("/exchange/json/serialize") - @ApiOperation(value = "Json serialization endpoint", httpMethod = "GET" ) + @Operation(description = "Json serialization endpoint" ) public void exchangeJsonSerialize(HttpServerExchange exchange) { try @@ -83,7 +86,7 @@ public void exchangeJsonSerialize(HttpServerExchange exchange) @GET @Path("/exchange/json/serializeToBytes") - @ApiOperation(value = "Json serialization with bytes endpoint", httpMethod = "GET" ) + @Operation(description = "Json serialization with bytes endpoint" ) public void exchangeJsonSerializeToBytes(HttpServerExchange exchange) { try @@ -99,7 +102,7 @@ public void exchangeJsonSerializeToBytes(HttpServerExchange exchange) @GET @Path("/exchange/user/json") - @ApiOperation(value = "User serialization endpoint", httpMethod = "GET", response = User.class ) + @Operation(description = "User serialization endpoint" ) public void exchangeUserJson(HttpServerExchange exchange) { response( new User(123L) ).applicationJson().send(exchange); @@ -108,7 +111,7 @@ public void exchangeUserJson(HttpServerExchange exchange) @GET @Path("/exchange/user/xml") @Produces((MediaType.APPLICATION_XML)) - @ApiOperation(value = "User serialization endpoint", httpMethod = "GET", response = User.class ) + @Operation(description = "User serialization endpoint" ) public void exchangeUserXml(HttpServerExchange exchange) { response( new User(123L) ).applicationXml().send(exchange); @@ -116,7 +119,7 @@ public void exchangeUserXml(HttpServerExchange exchange) @GET @Path("/response/user/json") - @ApiOperation(value = "User serialization endpoint", httpMethod = "GET" ) + @Operation(description = "User serialization endpoint" ) public ServerResponse responseUserJson(ServerRequest request) { User user = new User(123L); @@ -127,7 +130,7 @@ public ServerResponse responseUserJson(ServerRequest request) @GET @Path("/response/user/xml") @Produces((MediaType.APPLICATION_XML)) - @ApiOperation(value = "User serialization endpoint", httpMethod = "GET" ) + @Operation(description = "User serialization endpoint" ) public ServerResponse responseUserXml(ServerRequest request) { User user = new User(123L); @@ -139,7 +142,7 @@ public ServerResponse responseUserXml(ServerRequest request) @GET @Path("/exchange/plaintext") @Produces((MediaType.TEXT_PLAIN)) - @ApiOperation(value = "Plaintext endpoint", httpMethod = "GET" ) + @Operation(description = "Plaintext endpoint" ) public void exchangePlaintext(HttpServerExchange exchange) { response("Hello, World!").textPlain().send(exchange); @@ -149,7 +152,7 @@ public void exchangePlaintext(HttpServerExchange exchange) @GET @Path("/exchange/plaintext2") @Produces((MediaType.TEXT_PLAIN)) - @ApiOperation(value = "Plaintext endpoint 2", httpMethod = "GET" ) + @Operation(description = "Plaintext endpoint 2" ) public void exchangePlaintext2(HttpServerExchange exchange) { exchange.getResponseHeaders().put(io.undertow.util.Headers.CONTENT_TYPE, "text/plain"); @@ -159,7 +162,7 @@ public void exchangePlaintext2(HttpServerExchange exchange) @GET @Path("/response/plaintext") @Produces((MediaType.TEXT_PLAIN)) - @ApiOperation(value = "Plaintext endpoint", httpMethod = "GET" ) + @Operation(description = "Plaintext endpoint" ) public ServerResponse responsePlaintext(ServerRequest request) { return response("Hello, World!").textPlain(); @@ -168,7 +171,7 @@ public ServerResponse responsePlaintext(ServerRequest request) @GET @Path("/response/future/map") - @ApiOperation(value = "Future map endpoint", httpMethod = "GET" ) + @Operation(description = "Future map endpoint" ) public CompletableFuture>> responseFutureMap( ServerRequest request ) { Map map = ImmutableMap.of("message", "success"); @@ -177,7 +180,7 @@ public CompletableFuture>> responseFutureMap( @GET @Path("/response/map") - @ApiOperation(value = "Map endpoint", httpMethod = "GET" ) + @Operation(description = "Map endpoint" ) public ServerResponse> futureMap( ServerRequest request ) { Map map = ImmutableMap.of("message", "success"); @@ -188,7 +191,7 @@ public ServerResponse> futureMap( ServerRequest request ) @Path("/response/file/path") @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.MULTIPART_FORM_DATA) - @ApiOperation(value = "Upload file path endpoint", httpMethod = "POST" ) + @Operation(description = "Upload file path endpoint" ) public ServerResponse responseUploadFilePath(ServerRequest request, @FormParam("file") java.nio.file.Path file ) throws Exception { return response(ByteBuffer.wrap(Files.toByteArray(file.toFile()))).applicationOctetStream(); @@ -198,7 +201,7 @@ public ServerResponse responseUploadFilePath(ServerRequest request, @Path("/response/file/path/optional") @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.MULTIPART_FORM_DATA) - @ApiOperation(value = "Upload optional file path endpoint", httpMethod = "POST" ) + @Operation(description = "Upload optional file path endpoint" ) public ServerResponse responseUploadOptionalFilePath(ServerRequest request, @FormParam("file") Optional file ) throws Exception { if(file.isPresent()) @@ -215,7 +218,7 @@ public ServerResponse responseUploadOptionalFilePath(ServerRequest r @Path("/response/json/echo") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.MULTIPART_FORM_DATA) - @ApiOperation(value = "Echo json endpoint", httpMethod = "POST" ) + @Operation(description = "Echo json endpoint" ) public ServerResponse responseEchoJson(ServerRequest request, @FormParam("user") User user ) throws Exception { return response(user).applicationJson(); @@ -225,7 +228,7 @@ public ServerResponse responseEchoJson(ServerRequest request, @FormParam(" @Path("/response/json/beanparam") @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_JSON) - @ApiOperation(value = "Echo json inner class endpoint", httpMethod = "POST" ) + @Operation(description = "Echo json inner class endpoint" ) public ServerResponse responseInnerClassTest(ServerRequest request, @BeanParam User user ) throws Exception { return response(user).applicationJson(); @@ -235,7 +238,7 @@ public ServerResponse responseInnerClassTest(ServerRequest request, @BeanP @GET @Path("/generic/set") @Produces((MediaType.APPLICATION_JSON)) - @ApiOperation(value = "Generic set endpoint", httpMethod = "GET" ) + @Operation(description = "Generic set endpoint" ) public ServerResponse> genericSet( ServerRequest request, @QueryParam("ids") Set ids ) throws Exception { return response( ids ).applicationJson(); @@ -245,7 +248,7 @@ public ServerResponse> genericSet( ServerRequest request, @QueryParam @GET @Path("/optional/set") @Produces((MediaType.APPLICATION_JSON)) - @ApiOperation(value = "Generic optional set endpoint", httpMethod = "GET" ) + @Operation(description = "Generic optional set endpoint" ) public ServerResponse> genericOptionalSet( ServerRequest request, @QueryParam("ids") Optional> ids ) throws Exception { return response( ids.get() ).applicationJson(); @@ -254,7 +257,7 @@ public ServerResponse> genericOptionalSet( ServerRequest request, @Qu @GET @Path("/redirect/permanent") - @ApiOperation(value = "Permanent redirect endpoint", httpMethod = "GET" ) + @Operation(description = "Permanent redirect endpoint" ) @Produces(MediaType.WILDCARD) public ServerResponse testPermanentRedirect() { @@ -263,7 +266,7 @@ public ServerResponse testPermanentRedirect() @GET @Path("/redirect") - @ApiOperation(value = "Redirect endpoint", httpMethod = "GET" ) + @Operation(description = "Redirect endpoint" ) @Produces(MediaType.WILDCARD) public ServerResponse testRedirect() { @@ -275,8 +278,7 @@ public ServerResponse testRedirect() @Blocking @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ApiOperation(value = "Convert ids" ) - @Operation(description = "Convert ids") + @Operation(description = "Convert ids") public ServerResponse> listConversion( ServerRequest request, @BeanParam List ids ) throws Exception { @@ -289,8 +291,7 @@ public ServerResponse> listConversion( ServerRequest request, @BeanPa @Path("/response/file/bytebuffer") @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes("*/*") - @ApiOperation(value = "Upload file path endpoint" ) - @Operation(description = "Upload file path endpoint") + @Operation(description = "Upload file path endpoint") public ServerResponse responseUploadByteBuffer(ServerRequest request, @FormParam("file") ByteBuffer file ) throws Exception { @@ -301,8 +302,7 @@ public ServerResponse responseUploadByteBuffer(ServerRequest request @GET @Path("/response/debug") - @ApiOperation(value = "Debug endpoint" ) - @Operation(description = "Debug endpoint") + @Operation(description = "Debug endpoint") public ServerResponse> debugEndpoint(ServerRequest request) { try @@ -320,8 +320,7 @@ public ServerResponse> debugEndpoint(ServerRequest request) @GET @Path("/response/debug/blocking") @Blocking - @ApiOperation(value = "Debug blocking endpoint" ) - @Operation(description="Debug blocking endpoint") + @Operation(description="Debug blocking endpoint") public ServerResponse> debugBlockingEndpoint(ServerRequest request) { try @@ -336,8 +335,7 @@ public ServerResponse> debugBlockingEndpoint(ServerRequest re } @GET - @Path("/response/future/user") - @ApiOperation(value = "Future user endpoint" ) + @Path("/response/future/user") @Operation(description="Future user endpoint") @Produces((MediaType.APPLICATION_JSON)) public CompletableFuture> responseFutureUser() @@ -347,7 +345,7 @@ public CompletableFuture> responseFutureUser() @GET @Path("/response/parameters/complex/{pathLong}") - @ApiOperation(value = "Complex parameters", httpMethod = "GET") + @Operation(description = "Complex parameters" ) @Produces((MediaType.APPLICATION_JSON)) public ServerResponse> complexParameters( ServerRequest serverRequest, diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index b1c6fff..a32783a 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -17,7 +17,7 @@ - +