diff --git a/core/pom.xml b/core/pom.xml index bd9a513..d345c70 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -62,7 +62,7 @@ maven-surefire-plugin - 2.20.1 + org.apache.maven.plugins diff --git a/core/src/main/java/io/sinistral/proteus/ProteusApplication.java b/core/src/main/java/io/sinistral/proteus/ProteusApplication.java index d50afbe..ed1595a 100644 --- a/core/src/main/java/io/sinistral/proteus/ProteusApplication.java +++ b/core/src/main/java/io/sinistral/proteus/ProteusApplication.java @@ -14,6 +14,9 @@ import com.google.inject.name.Named; import com.typesafe.config.Config; import io.sinistral.proteus.modules.ConfigModule; +import io.sinistral.proteus.modules.JacksonModule; +import io.sinistral.proteus.server.Extractors; +import io.sinistral.proteus.server.ServerResponse; import io.sinistral.proteus.server.endpoints.EndpointInfo; import io.sinistral.proteus.server.handlers.HandlerGenerator; import io.sinistral.proteus.server.handlers.ServerDefaultHttpHandler; @@ -226,6 +229,8 @@ public boolean isRunning() public void buildServer() { + + for (Class controllerClass : registeredControllers) { HandlerGenerator generator = new HandlerGenerator("io.sinistral.proteus.controllers.handlers", controllerClass); diff --git a/core/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java b/core/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java index 16b57ec..b6a13ef 100644 --- a/core/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java +++ b/core/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java @@ -59,21 +59,32 @@ public void bindMappers() this.bind(XmlMapper.class).toInstance(xmlMapper); - ObjectMapper objectMapper = new ObjectMapper(); - - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true); - objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); - objectMapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true); - objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); - objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true); - objectMapper.registerModule(new AfterburnerModule()); - objectMapper.registerModule(new Jdk8Module()); - - this.bind(ObjectMapper.class).toInstance(objectMapper); + try { + + String className = config.getString("application.jacksonModule"); + + log.info("Installing JacksonModule " + className); + + Class clazz = (Class) Class.forName(className); + + AbstractModule module = clazz.newInstance(); + + install(module); + + } catch (Exception e) { + + this.binder().addError(e); + + log.error(e.getMessage(), e); + + install(new JacksonModule()); + + } + this.requestStaticInjection(Extractors.class); this.requestStaticInjection(ServerResponse.class); - } + + } @SuppressWarnings("unchecked") @Override diff --git a/core/src/main/java/io/sinistral/proteus/modules/ConfigModule.java b/core/src/main/java/io/sinistral/proteus/modules/ConfigModule.java index 09a5a26..0d1fd73 100644 --- a/core/src/main/java/io/sinistral/proteus/modules/ConfigModule.java +++ b/core/src/main/java/io/sinistral/proteus/modules/ConfigModule.java @@ -46,6 +46,8 @@ public ConfigModule() } } + + public ConfigModule(String configFile) { this.configFile = configFile; @@ -56,6 +58,16 @@ public ConfigModule(URL configURL) this.configURL = configURL; } + public Config getConfig() + { + return config; + } + + public void setConfig(Config config) + { + this.config = config; + } + @SuppressWarnings("unchecked") private void bindConfig(final Config config) { diff --git a/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java b/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java new file mode 100644 index 0000000..b66e00e --- /dev/null +++ b/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java @@ -0,0 +1,27 @@ +package io.sinistral.proteus.modules; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.module.afterburner.AfterburnerModule; +import com.google.inject.AbstractModule; + +public class JacksonModule extends AbstractModule +{ + @Override + protected void configure() + { + ObjectMapper objectMapper = new ObjectMapper(); + + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true); + objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); + objectMapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true); + objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true); + objectMapper.registerModule(new AfterburnerModule()); + objectMapper.registerModule(new Jdk8Module()); + + this.bind(ObjectMapper.class).toInstance(objectMapper); + } +} diff --git a/core/src/main/java/io/sinistral/proteus/server/MediaType.java b/core/src/main/java/io/sinistral/proteus/server/MediaType.java index cf8a13c..928b2ac 100644 --- a/core/src/main/java/io/sinistral/proteus/server/MediaType.java +++ b/core/src/main/java/io/sinistral/proteus/server/MediaType.java @@ -9,9 +9,14 @@ */ +import java.lang.reflect.Field; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; public class MediaType @@ -1210,6 +1215,11 @@ public static synchronized MediaType getByFileName(String filename) } } + public static synchronized MediaType getByMimeType(String type) + { + return Optional.ofNullable(getTypeMap().get(type)).orElse(MediaType.DEFAULT); + } + public String withCharset(String charset) { return charset != null ? String.format("%s; charset=%s", this.contentType, charset.toUpperCase()) : this.contentType; @@ -1262,6 +1272,30 @@ public String toString() return this.contentType; } + private static final Map TYPE_MAP = new HashMap<>(); + + public static Map getTypeMap() + { + if(TYPE_MAP.size() == 0) { + Class clazz = MediaType.class; + + Field[] fields = clazz.getDeclaredFields(); + + Arrays.stream(fields).filter(f -> f.getType().equals(MediaType.class)).forEach( f -> { + + try + { + MediaType mt = (MediaType)f.get(MediaType.class); + TYPE_MAP.put(mt.contentType,mt); + } catch( Exception e ) + { + e.printStackTrace(); + } + }); + } + + return TYPE_MAP; + } @Override public int hashCode() @@ -1307,5 +1341,4 @@ public String info() return toString(); } - } diff --git a/core/src/main/java/io/sinistral/proteus/server/exceptions/ServerException.java b/core/src/main/java/io/sinistral/proteus/server/exceptions/ServerException.java index f98a5d0..ef5e8a5 100644 --- a/core/src/main/java/io/sinistral/proteus/server/exceptions/ServerException.java +++ b/core/src/main/java/io/sinistral/proteus/server/exceptions/ServerException.java @@ -9,7 +9,7 @@ * @author jbauer * */ -public class ServerException extends Exception +public class ServerException extends RuntimeException { /** * diff --git a/core/src/main/resources/reference.conf b/core/src/main/resources/reference.conf index 3afcb62..610134f 100644 --- a/core/src/main/resources/reference.conf +++ b/core/src/main/resources/reference.conf @@ -22,6 +22,8 @@ application { defaultResponseListener = "io.sinistral.proteus.server.handlers.ServerDefaultResponseListener" + jacksonModule = "io.sinistral.proteus.modules.JacksonModule" + tmpdir = ${java.io.tmpdir}/${application.name} # path to default favicon file @@ -61,7 +63,6 @@ assets { openapi { - basePath= ${application.path}"/openapi" redocPath= "redoc" @@ -93,6 +94,10 @@ openapi { description="Default Server" } ] + + converterClasses = [ + + ] } diff --git a/core/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java b/core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java similarity index 98% rename from core/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java rename to core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java index 3a2f3ed..aa852b6 100644 --- a/core/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java +++ b/core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java @@ -1,7 +1,7 @@ /** * */ -package io.sinistral.proteus.swagger.test.controllers; +package io.sinistral.proteus.test.controllers; import static io.sinistral.proteus.server.ServerResponse.response; @@ -37,10 +37,9 @@ import com.google.inject.Singleton; import io.sinistral.proteus.annotations.Blocking; -import io.sinistral.proteus.annotations.Debug; import io.sinistral.proteus.server.ServerRequest; import io.sinistral.proteus.server.ServerResponse; -import io.sinistral.proteus.swagger.test.models.User; +import io.sinistral.proteus.test.models.User; import io.undertow.server.HttpServerExchange; diff --git a/core/src/test/java/io/sinistral/proteus/swagger/test/models/User.java b/core/src/test/java/io/sinistral/proteus/test/models/User.java similarity index 94% rename from core/src/test/java/io/sinistral/proteus/swagger/test/models/User.java rename to core/src/test/java/io/sinistral/proteus/test/models/User.java index 32d81c2..04e405e 100644 --- a/core/src/test/java/io/sinistral/proteus/swagger/test/models/User.java +++ b/core/src/test/java/io/sinistral/proteus/test/models/User.java @@ -1,7 +1,7 @@ /** * */ -package io.sinistral.proteus.swagger.test.models; +package io.sinistral.proteus.test.models; /** diff --git a/core/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java b/core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java similarity index 95% rename from core/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java rename to core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java index a65fab2..4f651d7 100644 --- a/core/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java +++ b/core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java @@ -1,11 +1,11 @@ /** * */ -package io.sinistral.proteus.swagger.test.server; +package io.sinistral.proteus.test.server; import java.util.List; -import io.sinistral.proteus.swagger.test.controllers.Tests; +import io.sinistral.proteus.test.controllers.Tests; import org.junit.runner.Description; import org.junit.runner.Result; import org.junit.runner.notification.RunListener; diff --git a/core/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java b/core/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java similarity index 98% rename from core/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java rename to core/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java index ae48ec1..64aaf53 100644 --- a/core/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java +++ b/core/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java @@ -1,7 +1,7 @@ /** * */ -package io.sinistral.proteus.swagger.test.server; +package io.sinistral.proteus.test.server; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.containsString; @@ -36,8 +36,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.restassured.http.ContentType; -import io.sinistral.proteus.swagger.test.models.User; -import io.sinistral.proteus.swagger.test.models.User.UserType; +import io.sinistral.proteus.test.models.User; +import io.sinistral.proteus.test.models.User.UserType; /* * import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*; diff --git a/core/src/test/resources/application.conf b/core/src/test/resources/application.conf index 9c55400..ddd47d4 100644 --- a/core/src/test/resources/application.conf +++ b/core/src/test/resources/application.conf @@ -22,6 +22,8 @@ application { defaultResponseListener = "io.sinistral.proteus.server.handlers.ServerDefaultResponseListener" + jacksonModule = "io.sinistral.proteus.modules.JacksonModule" + tmpdir = ${java.io.tmpdir}/${application.name} # path to default favicon file diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml index 8763c33..44a9e83 100644 --- a/core/src/test/resources/logback-test.xml +++ b/core/src/test/resources/logback-test.xml @@ -16,12 +16,8 @@ - - - + - - diff --git a/openapi/pom.xml b/openapi/pom.xml index 7badaa1..7cfa6a0 100644 --- a/openapi/pom.xml +++ b/openapi/pom.xml @@ -29,7 +29,7 @@ src/test/resources - src/test/java + src/test/java/sinistral/proteus/openapi **/*.java @@ -59,7 +59,7 @@ maven-surefire-plugin - 2.20.1 + org.apache.maven.plugins @@ -109,6 +109,14 @@ swagger-integration ${openapi.version} + + + org.zalando + jackson-datatype-money + 1.1.1 + test + + ${project.groupId} proteus-core diff --git a/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java b/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java index 0944968..b5981c2 100644 --- a/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java +++ b/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java @@ -58,7 +58,6 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context if ((rawClass != null) &&!resolvedType.isPrimitive()) { - // log.debug("resolvedType in " + resolvedType); if (rawClass.isAssignableFrom(ServerResponse.class)) { resolvedType = classType.containedType(0); @@ -69,14 +68,12 @@ else if (rawClass.isAssignableFrom(CompletableFuture.class)) if (futureCls.isAssignableFrom(ServerResponse.class)) { - // log.debug("class is assignable from ServerResponse"); final JavaType futureType = TypeFactory.defaultInstance().constructType(classType.containedType(0)); resolvedType = futureType.containedType(0); } else { - // log.debug("class is NOT assignable from ServerResponse"); resolvedType = classType.containedType(0); } } @@ -114,13 +111,11 @@ else if (resolvedType.getTypeName().contains("Optional")) annotatedType.setType(resolvedType); - // log.debug("resolvedType out " + resolvedType); } } try { - // log.info("Processing " + annotatedType + " " + classType + " " + annotatedType.getName()); return super.resolve(annotatedType, context, next); } catch (Exception e) { diff --git a/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerParameterExtension.java b/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerParameterExtension.java index 5d6c85b..2ba39a5 100644 --- a/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerParameterExtension.java +++ b/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerParameterExtension.java @@ -22,10 +22,19 @@ import io.swagger.v3.oas.models.parameters.Parameter; import org.apache.commons.lang3.StringUtils; -import javax.ws.rs.*; +import javax.ws.rs.BeanParam; +import javax.ws.rs.CookieParam; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.MatrixParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; /** * @author jbauer @@ -38,7 +47,8 @@ public class ServerParameterExtension extends AbstractOpenAPIExtension private static String COOKIE_PARAM = "cookie"; private static String PATH_PARAM = "path"; private static String FORM_PARAM = "form"; - final ObjectMapper mapper = Json.mapper(); + + final static ObjectMapper mapper = Json.mapper(); @Override public ResolvedParameter extractParameters(List annotations, Type type, Set typesToSkip, Components components, javax.ws.rs.Consumes classConsumes, diff --git a/openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java b/openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java index 09a2ca0..6fa5c5a 100644 --- a/openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java +++ b/openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java @@ -1,52 +1,18 @@ package io.sinistral.proteus.openapi.services; -import java.io.File; -import java.io.InputStream; - -import java.net.URL; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; -import java.util.jar.JarFile; -import java.util.stream.Collectors; - -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.MediaType; - -import io.sinistral.proteus.openapi.jaxrs2.Reader; -import io.sinistral.proteus.openapi.jaxrs2.ServerModelResolver; -import io.sinistral.proteus.openapi.jaxrs2.ServerParameterExtension; -import io.sinistral.proteus.services.DefaultService; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; - import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; - import com.typesafe.config.Config; - +import io.sinistral.proteus.openapi.jaxrs2.Reader; +import io.sinistral.proteus.openapi.jaxrs2.ServerModelResolver; +import io.sinistral.proteus.openapi.jaxrs2.ServerParameterExtension; import io.sinistral.proteus.server.endpoints.EndpointInfo; - +import io.sinistral.proteus.services.DefaultService; import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.jaxrs2.ext.OpenAPIExtensions; @@ -59,7 +25,6 @@ import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; - import io.undertow.server.HandlerWrapper; import io.undertow.server.HttpServerExchange; import io.undertow.server.RoutingHandler; @@ -69,6 +34,31 @@ import io.undertow.util.CanonicalPathUtils; import io.undertow.util.Headers; import io.undertow.util.Methods; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.MediaType; +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import java.util.jar.JarFile; +import java.util.stream.Collectors; /** * A service for generating and serving an OpenAPI v3 spec and ui. @@ -265,7 +255,15 @@ protected void generateSpec() throws Exception SwaggerConfiguration config = new SwaggerConfiguration().resourceClasses(classes.stream().map(Class::getName).collect(Collectors.toSet())).openAPI(openApi); - config.setModelConverterClassess(Collections.singleton(ServerModelResolver.class.getName())); + Set modelConverterClasses = new HashSet<>(); + + modelConverterClasses.add(ServerModelResolver.class.getName()); + + List additionalConverterClasses = openAPIConfig.getStringList("converterClasses"); + + modelConverterClasses.addAll(additionalConverterClasses); + + config.setModelConverterClassess(modelConverterClasses); OpenApiContext ctx = new GenericOpenApiContext().openApiConfiguration(config) .openApiReader(new Reader(config)) diff --git a/openapi/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/controllers/OpenAPITests.java similarity index 88% rename from openapi/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java rename to openapi/src/test/java/io/sinistral/proteus/openapi/test/controllers/OpenAPITests.java index 71bf8dd..4796233 100644 --- a/openapi/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/controllers/OpenAPITests.java @@ -1,7 +1,7 @@ /** * */ -package io.sinistral.proteus.swagger.test.controllers; +package io.sinistral.proteus.openapi.test.controllers; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; @@ -9,6 +9,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import io.sinistral.proteus.annotations.Blocking; +import io.sinistral.proteus.openapi.test.models.Pojo; import io.sinistral.proteus.server.ServerRequest; import io.sinistral.proteus.server.ServerResponse; import io.swagger.v3.oas.annotations.Operation; @@ -16,14 +17,26 @@ import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tags; import io.undertow.server.HttpServerExchange; +import org.javamoney.moneta.Money; -import javax.ws.rs.*; +import javax.ws.rs.BeanParam; +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.io.File; import java.nio.ByteBuffer; import java.sql.Timestamp; import java.time.Instant; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import static io.sinistral.proteus.server.ServerResponse.response; @@ -38,7 +51,7 @@ @Produces((MediaType.APPLICATION_JSON)) @Consumes((MediaType.MEDIA_TYPE_WILDCARD)) @Singleton -public class Tests +public class OpenAPITests { private static final ByteBuffer buffer; static { @@ -127,8 +140,27 @@ public ServerResponse> genericSet( ServerRequest request, @QueryParam { return response( ids ).applicationJson(); } - - + + + @GET + @Path("types/money") + @Produces((MediaType.APPLICATION_JSON)) + @Operation(description = "Money type endpoint" ) + public ServerResponse getMoney(ServerRequest request ) throws Exception + { + return response( Money.of(123.23,"USD") ).applicationJson(); + } + + + @GET + @Path("types/pojo") + @Produces((MediaType.APPLICATION_JSON)) + @Operation(description = "Pojo type endpoint" ) + public ServerResponse getPojo(ServerRequest request ) throws Exception + { + return response( new Pojo(100L,"John Doe") ).applicationJson(); + } + @POST @Path("generic/set/bean") @Produces((MediaType.APPLICATION_JSON)) diff --git a/openapi/src/test/java/io/sinistral/proteus/openapi/test/converters/MoneyModelConverter.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/converters/MoneyModelConverter.java new file mode 100644 index 0000000..b4c25fe --- /dev/null +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/converters/MoneyModelConverter.java @@ -0,0 +1,151 @@ +package io.sinistral.proteus.openapi.test.converters; + + +import com.fasterxml.jackson.databind.JavaType; +import com.google.common.collect.ImmutableMap; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverterContext; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.media.NumberSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import org.javamoney.moneta.Money; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class MoneyModelConverter implements ModelConverter +{ + + private final Schema schema = new MoneySchema(); + + @Override + public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) { + + JavaType _type = Json.mapper().constructType(type.getType()); + + if(_type != null && (_type.getRawClass().equals(Money.class))) + { + context.defineModel("Money",schema, type, null); + + return schema; + } + + if (type.getType() instanceof Class) { + + Class cls = (Class) type.getType(); + + if(cls.isAssignableFrom(Money.class)) + { + context.defineModel("Money",schema, type, null); + + return schema; + } + } + else if(type.getType().getTypeName().equals("[simple type, class org.javamoney.moneta.Money]")) + { + context.defineModel("Money",schema, type, null); + + return schema; + } + else if (type.isSchemaProperty()) { + + _type = Json.mapper().constructType(type.getType()); + + if (_type != null) { + Class cls = _type.getRawClass(); + if (Money.class.isAssignableFrom(cls)) { + + context.defineModel("Money",schema, type, null); + + return schema; + } + } + } + + if (chain.hasNext()) { + return chain.next().resolve(type, context, chain); + } + + return null; + + } + + + public static class MoneySchema extends Schema + { + private String _type = "object"; + + private String _$ref = null; + + private String _description = "A monetary amount"; + + private Map _properties = ImmutableMap.of("amount",new NumberSchema(),"currency",new StringSchema()); + + private List _required = Arrays.asList("amount","currency"); + + public MoneySchema() + { + super(); + super.setName("Money"); + super.setType("object"); + super.set$ref(_$ref); + super.description(_description); + super.properties(_properties); + super.required(_required); + } + + @Override + protected Money cast(Object value) + { + if (value != null) { + try { + if (value instanceof Money) { + return (Money) value; + } + } catch (Exception e) { + } + } + return null; + } + + @Override + public boolean equals(java.lang.Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MoneySchema MoneySchema = (MoneySchema) o; + return Objects.equals(this._type, MoneySchema._type) && + Objects.equals(this._properties, MoneySchema._properties) && + Objects.equals(this._description, MoneySchema._description) && + super.equals(o); + } + + @Override + public int hashCode() + { + return Objects.hash(_type, _properties, super.hashCode()); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) + { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + } +} diff --git a/openapi/src/test/java/io/sinistral/proteus/openapi/test/models/Pojo.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/models/Pojo.java new file mode 100644 index 0000000..5d9e8af --- /dev/null +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/models/Pojo.java @@ -0,0 +1,40 @@ +package io.sinistral.proteus.openapi.test.models; + +public class Pojo +{ + + public Long id; + + public String name; + + + public Pojo(Long id, String name) + { + this.id = id; + this.name = name; + } + + public Pojo() + { + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } +} diff --git a/openapi/src/test/java/io/sinistral/proteus/openapi/test/modules/JacksonModule.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/modules/JacksonModule.java new file mode 100644 index 0000000..4bf0036 --- /dev/null +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/modules/JacksonModule.java @@ -0,0 +1,35 @@ +package io.sinistral.proteus.openapi.test.modules; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.module.afterburner.AfterburnerModule; +import com.google.inject.AbstractModule; +import com.google.inject.Singleton; +import org.zalando.jackson.datatype.money.MoneyModule; + +@Singleton +public class JacksonModule extends AbstractModule +{ + + @Override + protected void configure() + { + + ObjectMapper objectMapper = new ObjectMapper(); + + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true); + objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); + objectMapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH,true); + objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true); + + objectMapper.registerModule(new MoneyModule()) + .registerModule(new AfterburnerModule()) + .registerModule(new Jdk8Module()); + + this.bind(ObjectMapper.class).toInstance(objectMapper); + + } +} \ No newline at end of file diff --git a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/OpenAPIDefaultServer.java similarity index 84% rename from openapi/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java rename to openapi/src/test/java/io/sinistral/proteus/openapi/test/server/OpenAPIDefaultServer.java index 5d1ea93..fbb6e1d 100644 --- a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/OpenAPIDefaultServer.java @@ -1,13 +1,13 @@ /** * */ -package io.sinistral.proteus.swagger.test.server; +package io.sinistral.proteus.openapi.test.server; import io.restassured.RestAssured; import io.sinistral.proteus.ProteusApplication; import io.sinistral.proteus.openapi.services.OpenAPIService; +import io.sinistral.proteus.openapi.test.controllers.OpenAPITests; import io.sinistral.proteus.services.AssetsService; -import io.sinistral.proteus.swagger.test.controllers.Tests; import org.junit.runner.Description; import org.junit.runner.Result; import org.junit.runner.notification.RunListener; @@ -22,9 +22,9 @@ /** * @author jbauer */ -public class DefaultServer extends BlockJUnit4ClassRunner +public class OpenAPIDefaultServer extends BlockJUnit4ClassRunner { - private static Logger log = LoggerFactory.getLogger(DefaultServer.class.getCanonicalName()); + private static Logger log = LoggerFactory.getLogger(OpenAPIDefaultServer.class.getCanonicalName()); private static boolean first = true; @@ -32,7 +32,7 @@ public class DefaultServer extends BlockJUnit4ClassRunner * @param clazz * @throws InitializationError */ - public DefaultServer(Class clazz) throws InitializationError + public OpenAPIDefaultServer(Class clazz) throws InitializationError { super(clazz); } @@ -73,9 +73,10 @@ private static void runInternal(final RunNotifier notifier) final ProteusApplication app = new ProteusApplication(); app.addService(OpenAPIService.class); + app.addService(AssetsService.class); - app.addController(Tests.class); + app.addController(OpenAPITests.class); app.start(); diff --git a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/TestOpenAPIControllerEndpoints.java similarity index 71% rename from openapi/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java rename to openapi/src/test/java/io/sinistral/proteus/openapi/test/server/TestOpenAPIControllerEndpoints.java index c91cb8d..9961e44 100644 --- a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/TestOpenAPIControllerEndpoints.java @@ -1,12 +1,12 @@ /** * */ -package io.sinistral.proteus.swagger.test.server; +package io.sinistral.proteus.openapi.test.server; -import static io.restassured.RestAssured.given; -import static io.restassured.RestAssured.when; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.File; import java.nio.file.Files; @@ -15,19 +15,14 @@ import java.util.Set; import java.util.stream.LongStream; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import static io.restassured.RestAssured.when; +import static org.hamcrest.CoreMatchers.equalTo; -/* - * import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*; - */ /** * @author jbauer */ -@RunWith(DefaultServer.class) -public class TestControllerEndpoints +@RunWith(OpenAPIDefaultServer.class) +public class TestOpenAPIControllerEndpoints { private File file = null; @@ -66,6 +61,18 @@ public void testOpenAPIDocs() when().get("openapi.yaml").then().statusCode(200); } + @Test + public void testPojoType() + { + when().get("tests/types/pojo").then().statusCode(200).body("id", equalTo(100),"name", equalTo("John Doe")); + } + + @Test + public void testMoneyType() + { + when().get("tests/types/money").then().statusCode(200).body("amount", equalTo(123.23f),"currency", equalTo("USD")); + } + @Test public void testSecurityRequirementEndpoint() diff --git a/openapi/src/test/resources/application.conf b/openapi/src/test/resources/application.conf new file mode 100644 index 0000000..098b42b --- /dev/null +++ b/openapi/src/test/resources/application.conf @@ -0,0 +1,89 @@ + +application { + + env = dev + + version = "1.0" + + name="proteus" + + path = "/v1" + + host = "localhost" + + ports { + http = 0 + # https = 8443 + } + + charset = UTF-8 + + fallbackHandler = "io.sinistral.proteus.server.handlers.ServerFallbackHandler" + + defaultResponseListener = "io.sinistral.proteus.server.handlers.ServerDefaultResponseListener" + + jacksonModule = "io.sinistral.proteus.openapi.test.modules.JacksonModule" + + tmpdir = ${java.io.tmpdir}/${application.name} + + # path to default favicon file + favicon = "/io/sinistral/proteus/favicon.ico" + +} + +api.version="v1" + +openapi.converterClasses = ["io.sinistral.proteus.openapi.test.converters.MoneyModelConverter"] + +globalHeaders +{ +# Access-Control-Allow-Origin: "*" +# Access-Control-Allow-Methods: "*" +# Access-Control-Allow-Headers: "*" + Server = ${application.name} +} + +assets { + # the base path assets will be server from + path = "/public" + # the directory to load the assets from + dir = "./assets" + cache { + # cache timeout for the assets + time = 500 + } +} + +undertow +{ + server { + enableHttp2 = false + alwaysSetDate = true + alwaysSetKeepAlive = false + recordRequestStartTime = false + maxEntitySize = 100M + bufferPipelinedData = false + } + + socket { + backlog = 10000 + } + + + ssl { + enabled=false + keystorePath="development.jks" + truststorePath="development.ts" + keystorePassword="password" + truststorePassword="password" + } + + enableHttp2=false + # x AvailableProcessors + ioThreadsMultiplier = 2 + workerThreadMultiplier = 8 + bufferSize = 16K + directBuffers = true +} + + \ No newline at end of file diff --git a/openapi/src/test/resources/development.jks b/openapi/src/test/resources/development.jks new file mode 100644 index 0000000..96d0454 Binary files /dev/null and b/openapi/src/test/resources/development.jks differ diff --git a/openapi/src/test/resources/development.ts b/openapi/src/test/resources/development.ts new file mode 100644 index 0000000..3dfea06 Binary files /dev/null and b/openapi/src/test/resources/development.ts differ diff --git a/openapi/src/test/resources/logback-test.xml b/openapi/src/test/resources/logback-test.xml index aba346f..733e338 100644 --- a/openapi/src/test/resources/logback-test.xml +++ b/openapi/src/test/resources/logback-test.xml @@ -16,12 +16,11 @@ - - - + + " diff --git a/pom.xml b/pom.xml index 0d93f2b..5780273 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.0.1 attach-sources @@ -70,10 +70,14 @@ + org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + + 8 + + 3.1.0 attach-javadocs @@ -81,10 +85,11 @@ jar - -Xdoclint:none + none + @@ -130,22 +135,41 @@ org.apache.maven.plugins maven-compiler-plugin - 3.5.1 + 3.8.1 1.8 1.8 -parameters + - + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-versions + + enforce + + + + + 1.8 + + + + + + maven-surefire-plugin - 2.20.1 + 3.0.0-M3 org.apache.maven.surefire surefire-junit47 - 2.20.1 + 3.0.0-M3 diff --git a/swagger/pom.xml b/swagger/pom.xml index ff0a0fa..4406a84 100644 --- a/swagger/pom.xml +++ b/swagger/pom.xml @@ -58,7 +58,7 @@ maven-surefire-plugin - 2.20.1 + org.apache.maven.plugins diff --git a/swagger/src/main/java/io/sinistral/proteus/swagger/services/SwaggerService.java b/swagger/src/main/java/io/sinistral/proteus/swagger/services/SwaggerService.java index 3bf5a2a..c88e217 100644 --- a/swagger/src/main/java/io/sinistral/proteus/swagger/services/SwaggerService.java +++ b/swagger/src/main/java/io/sinistral/proteus/swagger/services/SwaggerService.java @@ -187,7 +187,7 @@ public void generateSwaggerSpec() throws Exception // String name = apiKeyConfig.getString("name"); // String value = apiKeyConfig.getString("value"); // -// io.swagger.models.auth.In keyLocation = io.swagger.models.auth.In.valueOf(apiKeyConfig.getString("in")); +// io.swagger.converters.auth.In keyLocation = io.swagger.converters.auth.In.valueOf(apiKeyConfig.getString("in")); // // final Predicate predicate; // diff --git a/swagger/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java b/swagger/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java index c83bfff..fd4c29c 100644 --- a/swagger/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java +++ b/swagger/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java @@ -15,13 +15,23 @@ import io.swagger.annotations.ApiOperation; import io.undertow.server.HttpServerExchange; -import javax.ws.rs.*; +import javax.ws.rs.BeanParam; +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.io.File; import java.nio.ByteBuffer; import java.sql.Timestamp; import java.time.Instant; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import static io.sinistral.proteus.server.ServerResponse.response; diff --git a/swagger/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java b/swagger/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java index 131200b..178fdc2 100644 --- a/swagger/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java +++ b/swagger/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java @@ -3,31 +3,26 @@ */ package io.sinistral.proteus.swagger.test.server; -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import io.restassured.http.ContentType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.File; -import java.io.InputStream; import java.nio.file.Files; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.stream.LongStream; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import io.restassured.http.ContentType; -import org.slf4j.LoggerFactory; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; /* * import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*; */ + /** * @author jbauer */ diff --git a/swagger/src/test/resources/logback-test.xml b/swagger/src/test/resources/logback-test.xml index aba346f..91e240d 100644 --- a/swagger/src/test/resources/logback-test.xml +++ b/swagger/src/test/resources/logback-test.xml @@ -16,11 +16,8 @@ - - -