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 6889df0..b6a13ef 100644 --- a/core/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java +++ b/core/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java @@ -81,6 +81,9 @@ public void bindMappers() } + this.requestStaticInjection(Extractors.class); + this.requestStaticInjection(ServerResponse.class); + } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java b/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java index fa5a7f0..b66e00e 100644 --- a/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java +++ b/core/src/main/java/io/sinistral/proteus/modules/JacksonModule.java @@ -5,8 +5,6 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.module.afterburner.AfterburnerModule; import com.google.inject.AbstractModule; -import io.sinistral.proteus.server.Extractors; -import io.sinistral.proteus.server.ServerResponse; public class JacksonModule extends AbstractModule { @@ -25,8 +23,5 @@ protected void configure() objectMapper.registerModule(new Jdk8Module()); this.bind(ObjectMapper.class).toInstance(objectMapper); - - this.requestStaticInjection(Extractors.class); - this.requestStaticInjection(ServerResponse.class); } } 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/openapi/pom.xml b/openapi/pom.xml index 7badaa1..1758cea 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 @@ -109,6 +109,13 @@ swagger-integration ${openapi.version} + + + org.zalando + jackson-datatype-money + 1.1.1 + + ${project.groupId} proteus-core diff --git a/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/Reader.java b/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/Reader.java index 69522a4..b3e3d7d 100644 --- a/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/Reader.java +++ b/openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/Reader.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; import com.fasterxml.jackson.databind.type.TypeFactory; +import io.sinistral.proteus.openapi.models.MoneyModelConverter; import io.sinistral.proteus.server.ServerRequest; import io.sinistral.proteus.server.ServerResponse; import io.swagger.v3.core.converter.AnnotatedType; @@ -1373,11 +1374,19 @@ else if (classType.getRawClass().isAssignableFrom(CompletableFuture.class)) } } - ResolvedSchema resolvedSchema = ModelConverters.getInstance() + ModelConverters converters = ModelConverters.getInstance(); + + converters.addConverter(new ServerModelResolver()); + converters.addConverter(new MoneyModelConverter()); + + + ResolvedSchema resolvedSchema = converters .resolveAsResolvedSchema(new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonViewAnnotation)); if (resolvedSchema.schema != null) { + System.out.println("resolvedSchema: " + resolvedSchema.schema); + Schema returnTypeSchema = resolvedSchema.schema; Content content = new Content(); MediaType mediaType = new MediaType().schema(returnTypeSchema); 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..0d364f5 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 @@ -8,12 +8,15 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.type.TypeFactory; +import io.sinistral.proteus.openapi.models.MoneyModelConverter; import io.sinistral.proteus.server.ServerResponse; 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.Schema; +import org.javamoney.moneta.Money; +import org.zalando.jackson.datatype.money.MoneyModule; import java.io.File; import java.lang.annotation.Annotation; @@ -30,9 +33,19 @@ public class ServerModelResolver extends io.swagger.v3.core.jackson.ModelResolve { private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ServerModelResolver.class.getCanonicalName()); + static final ObjectMapper mapper = Json.mapper(); + + static + { + mapper.registerModule(new MoneyModule()); + } + public ServerModelResolver() { - super(Json.mapper()); + super(mapper); + + + System.out.println("not from super"); } /** @@ -40,7 +53,8 @@ public ServerModelResolver() */ public ServerModelResolver(ObjectMapper mapper) { - super(mapper); + super(ServerModelResolver.mapper); + } /* @@ -83,6 +97,11 @@ else if (rawClass.isAssignableFrom(CompletableFuture.class)) if (resolvedType != null) { +// if (resolvedType.getTypeName().contains("org.javamoney.moneta.Money")) +// { +// resolvedType = TypeFactory.defaultInstance().constructFromCanonical(Money.class.getName()); +// } + if (resolvedType.getTypeName().contains("java.lang.Void")) { resolvedType = TypeFactory.defaultInstance().constructFromCanonical(Void.class.getName()); @@ -120,8 +139,24 @@ else if (resolvedType.getTypeName().contains("Optional")) try { + // log.info("Processing " + annotatedType + " " + classType + " " + annotatedType.getName()); - return super.resolve(annotatedType, context, next); + + if(annotatedType.getType().getTypeName().contains("org.javamoney.moneta.Money")) + { + MoneyModelConverter.MoneySchema schema = new MoneyModelConverter.MoneySchema(); + + context.defineModel("money",schema); + + return schema; + } + else { + + Schema schema = super.resolve(annotatedType, context, next); + + return schema; + } + } 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..56e9efb 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 @@ -4,6 +4,8 @@ */ package io.sinistral.proteus.openapi.jaxrs2; +import org.zalando.jackson.datatype.money.MoneyModule; + import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; @@ -38,7 +40,13 @@ 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(); + + static + { + mapper.registerModule(new MoneyModule()); + } @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/models/MoneyModelConverter.java b/openapi/src/main/java/io/sinistral/proteus/openapi/models/MoneyModelConverter.java new file mode 100644 index 0000000..f37539e --- /dev/null +++ b/openapi/src/main/java/io/sinistral/proteus/openapi/models/MoneyModelConverter.java @@ -0,0 +1,157 @@ +package io.sinistral.proteus.openapi.models; + + +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 +{ + + @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))) + { + return new MoneySchema(); + } + + if (type.getType() instanceof Class) { + + System.out.println("MoneyModelConverter resolving class: " + ((Class) type.getType()).getName()); + + Class cls = (Class) type.getType(); + + if(cls.isAssignableFrom(Money.class)) + { + return new MoneySchema(); + } + } + else if(type.getType().getTypeName().equals("[simple type, class org.javamoney.moneta.Money]")) + { + System.out.println("is org.javamoney.moneta.Money"); + return new MoneySchema(); + } + else if (type.isSchemaProperty()) { + _type = Json.mapper().constructType(type.getType()); + + if (_type != null) { + Class cls = _type.getRawClass(); + if (Money.class.isAssignableFrom(cls)) { + return new MoneySchema(); + } + } + } + + if (chain.hasNext()) { + System.out.println("skipped resolving " + _type); + + return chain.next().resolve(type, context, chain); + } + + return null; + + } + + + public static class MoneySchema extends Schema + { + private String _type = "object"; + + private String _$ref = null;// "#/components/schemas/Money"; + + private String _description = "A monetary amount"; + + private Map _properties = ImmutableMap.of("number",new NumberSchema(),"currency",new StringSchema()); + + private List _required = Arrays.asList("number","currency"); + + public MoneySchema() + { + super(); + super.setName("Money"); + super.setType("object"); + super.set$ref(_$ref); + super.description(_description); + super.properties(_properties); + } + + @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) && + super.equals(o); + } + + @Override + public int hashCode() + { + return Objects.hash(_type, _properties, super.hashCode()); + } + +// @Override +// public String toString() +// { +// StringBuilder sb = new StringBuilder(); +// sb.append("class MoneySchema {\n"); +// sb.append(" ").append(toIndentedString(super.toString())).append("\n"); +// sb.append(" title: ").append(toIndentedString(getTitle())).append("\n"); +// sb.append(" type: ").append(toIndentedString(_type)).append("\n"); +// sb.append(toIndentedString(_properties.toString())).append("\n"); +// sb.append(" }").append("\n"); +// +// sb.append("}"); +// return sb.toString(); +// } + + /** + * 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/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java b/openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java index 09a2ca0..0d5aeeb 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 @@ -12,7 +12,9 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -27,6 +29,7 @@ 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.openapi.models.MoneyModelConverter; import io.sinistral.proteus.services.DefaultService; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -265,7 +268,7 @@ 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())); + config.setModelConverterClassess(new HashSet<>(Arrays.asList(ServerModelResolver.class.getName()))); OpenApiContext ctx = new GenericOpenApiContext().openApiConfiguration(config) .openApiReader(new Reader(config)) diff --git a/openapi/src/test/java/io/sinistral/proteus/openapi/test/controllers/OpenAPITests.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/controllers/OpenAPITests.java new file mode 100644 index 0000000..9ddefb8 --- /dev/null +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/controllers/OpenAPITests.java @@ -0,0 +1,316 @@ +/** + * + */ +package io.sinistral.proteus.openapi.test.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; +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; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +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.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.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; + +/** + * @author jbauer + * + */ + +@Tags({@Tag(name = "tests")}) +@Path("/tests") +@Produces((MediaType.APPLICATION_JSON)) +@Consumes((MediaType.MEDIA_TYPE_WILDCARD)) +@Singleton +public class OpenAPITests +{ + private static final ByteBuffer buffer; + static { + String message = "Hello, World!"; + byte[] messageBytes = message.getBytes(java.nio.charset.StandardCharsets.US_ASCII); + buffer = ByteBuffer.allocateDirect(messageBytes.length); + buffer.put(messageBytes); + buffer.flip(); + } + + @Inject + protected ObjectMapper objectMapper; + + + @GET + @Path("exchange/plaintext") + @Produces((MediaType.TEXT_PLAIN)) + @Operation(description = "Plaintext endpoint" ) + public void exchangePlaintext(HttpServerExchange exchange) + { + response("Hello, World!").textPlain().send(exchange); + + } + + + @GET + @Path("response/plaintext") + @Produces((MediaType.TEXT_PLAIN)) + @Operation(description = "Plaintext endpoint" ) + public ServerResponse responsePlaintext(ServerRequest request) + { + return response("Hello, World!").textPlain(); + + } + + @GET + @Path("response/future/map") + @Operation(description = "Future map endpoint" ) + public CompletableFuture>> responseFutureMap( ServerRequest request ) + { + Map map = ImmutableMap.of("message", "success"); + return CompletableFuture.completedFuture(response( map ).applicationJson()); + } + + @GET + @Path("response/map") + @Operation(description = "Map endpoint" ) + public ServerResponse> futureMap( ServerRequest request ) + { + Map map = ImmutableMap.of("message", "success"); + return response( map ).applicationJson(); + } + + @POST + @Path("response/file/path") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @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(); + } + + @POST + @Path("response/file/path/optional") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Operation(description = "Upload optional file path endpoint" ) + public ServerResponse responseUploadOptionalFilePath(ServerRequest request, @FormParam("file") Optional file ) throws Exception + { + if(file.isPresent()) + { + return response(ByteBuffer.wrap(Files.toByteArray(file.get().toFile()))).applicationOctetStream(); + } + else + { + return response().noContent(); + } + } + + @GET + @Path("generic/set") + @Produces((MediaType.APPLICATION_JSON)) + @Operation(description = "Generic set endpoint" ) + public ServerResponse> genericSet( ServerRequest request, @QueryParam("ids") Set ids ) throws Exception + { + 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(100.0,"USD") ).applicationJson(); + } + + @GET + @Path("types/pojo2") + @Produces((MediaType.APPLICATION_JSON)) + @Operation(description = "Money type endpoint" ) + public ServerResponse getPojo2(ServerRequest request ) throws Exception + { + return response( new Pojo(100L,"USD") ).applicationJson(); + } + + @GET + @Path("types/pojo") + @Produces((MediaType.APPLICATION_JSON)) + @Operation(description = "Money type endpoint" ) + public ServerResponse getPojo(ServerRequest request ) throws Exception + { + return response( new Pojo(100L,"USD") ).applicationJson(); + } + + @POST + @Path("generic/set/bean") + @Produces((MediaType.APPLICATION_JSON)) + @Consumes(MediaType.APPLICATION_JSON) + @Operation(description = "Generic bean set endpoint" ) + public ServerResponse> genericBeanSet( ServerRequest request, @BeanParam Set ids ) throws Exception + { + return response( ids ).applicationJson(); + } + + + @POST + @Path("generic/list/bean") + @Produces((MediaType.APPLICATION_JSON)) + @Consumes(MediaType.APPLICATION_JSON) + + @Operation(description = "Generic bean list endpoint" ) + public ServerResponse> genericBeanList( ServerRequest request, @BeanParam List ids ) throws Exception + { + return response( ids ).applicationJson(); + } + + @GET + @Path("optional/set") + @Produces((MediaType.APPLICATION_JSON)) + @Operation(description = "Generic optional set endpoint" ) + public ServerResponse> genericOptionalSet( ServerRequest request, @QueryParam("ids") Optional> ids ) throws Exception + { + return response( ids.get() ).applicationJson(); + } + + + @POST + @Path("response/parse/ids") + @Blocking + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Operation(description = "Convert ids") + public ServerResponse> listConversion( ServerRequest request, @BeanParam List ids ) throws Exception + { + + return response( ids ).applicationJson(); + + + } + + @GET + @Path("response/parse/timestamp") + @Blocking + @Produces(MediaType.TEXT_PLAIN) + @Operation(description = "Convert timestamp") + public ServerResponse timestampConversion( ServerRequest request, @QueryParam("timestamp") Timestamp timestamp ) throws Exception + { + return response().body(timestamp.toString()).textPlain(); + } + + @GET + @Path("response/parse/instant") + @Blocking + @Produces(MediaType.TEXT_PLAIN) + @Operation(description = "Convert instant") + public ServerResponse instantConversion( ServerRequest request, @QueryParam("instant") Instant instant ) throws Exception + { + + return response().body(instant.toString()).textPlain(); + + + } + + @POST + @Path("response/bytebuffer") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes("*/*") + @Operation(description = "Upload file path endpoint") + public ServerResponse responseUploadByteBuffer(ServerRequest request, @FormParam("file") ByteBuffer file ) throws Exception + { + + return response(file).applicationOctetStream(); + + + } + + @POST + @Path("response/file") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes("*/*") + @Operation(description = "Upload file path endpoint") + public ServerResponse responseUploadFile(ServerRequest request, @FormParam("file") File file ) throws Exception + { + + ByteBuffer response = ByteBuffer.wrap(Files.asByteSource(file).read()); + + + return response(response).applicationOctetStream(); + + + } + + @GET + @Path("response/debug") + @Operation(description = "Debug endpoint") + public ServerResponse> debugEndpoint(ServerRequest request) + { + try + { + Map map = ImmutableMap.of("message", "Hello, World!"); + + return response( map ).applicationJson(); + } catch(Exception e) + { + return response().badRequest(e); + } + } + + @GET + @Path("response/debug/blocking") + @Blocking + @Operation(description="Debug blocking endpoint") + public ServerResponse> debugBlockingEndpoint(ServerRequest request) + { + try + { + Map map = ImmutableMap.of("message", "Hello, World!"); + + return response( map ).applicationJson(); + } catch(Exception e) + { + return response().badRequest(e); + } + } + + + @GET + @SecurityRequirement(name = "testRequirement") + @Path("secure/resource") + @Operation(description="Secure resource") + @Produces(MediaType.APPLICATION_JSON) + public ServerResponse> responseSecureContext() + { + Map responseMap = new HashMap<>(); + responseMap.put("secure",true); + + return response(responseMap); + } +} 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/server/OpenAPIDefaultServer.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/OpenAPIDefaultServer.java new file mode 100644 index 0000000..d22cac3 --- /dev/null +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/OpenAPIDefaultServer.java @@ -0,0 +1,129 @@ +/** + * + */ +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 org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * @author jbauer + */ +public class OpenAPIDefaultServer extends BlockJUnit4ClassRunner +{ + private static Logger log = LoggerFactory.getLogger(OpenAPIDefaultServer.class.getCanonicalName()); + + private static boolean first = true; + + /** + * @param clazz + * @throws InitializationError + */ + public OpenAPIDefaultServer(Class clazz) throws InitializationError + { + super(clazz); + } + + @Override + public void run(final RunNotifier notifier) + { + notifier.addListener(new RunListener() + { + @Override + public void testStarted(Description description) throws Exception + { + + super.testStarted(description); + } + + @Override + public void testFinished(Description description) throws Exception + { + + super.testFinished(description); + } + }); + + runInternal(notifier); + + super.run(notifier); + } + + private static void runInternal(final RunNotifier notifier) + { + + if (first) + { + + first = false; + + final ProteusApplication app = new ProteusApplication(); + + app.addService(OpenAPIService.class); + app.addService(AssetsService.class); + + app.addController(OpenAPITests.class); + + app.start(); + + int port = 0; + + try + { + Thread.sleep(5000); + + System.out.println(app.getPorts()); + + List ports = app.getPorts(); + + port = ports.get(0); + + } catch (Exception e) + { + e.printStackTrace(); + } + + + + RestAssured.baseURI = String.format("http://localhost:%d/v1",port); + + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + + while (!app.isRunning()) + { + try + { + Thread.sleep(100L); + } catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + notifier.addListener(new RunListener() + { + @Override + public void testRunFinished(final Result result) throws Exception + { + app.shutdown(); + }; + }); + } + + } + +} diff --git a/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/TestOpenAPIControllerEndpoints.java b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/TestOpenAPIControllerEndpoints.java new file mode 100644 index 0000000..7f003ce --- /dev/null +++ b/openapi/src/test/java/io/sinistral/proteus/openapi/test/server/TestOpenAPIControllerEndpoints.java @@ -0,0 +1,107 @@ +/** + * + */ +package io.sinistral.proteus.openapi.test.server; + +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; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.stream.LongStream; + +import static io.restassured.RestAssured.when; + +/* + * import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*; + */ + +/** + * @author jbauer + */ +@RunWith(OpenAPIDefaultServer.class) +public class TestOpenAPIControllerEndpoints +{ + + private File file = null; + + private Set idSet = new HashSet<>(); + + @Before + public void setUp() + { + try + { + byte[] bytes = new byte[8388608]; + Random random = new Random(); + random.nextBytes(bytes); + + file = Files.createTempFile("test-asset", ".mp4").toFile(); + + LongStream.range(1L,10L).forEach( l -> { + + idSet.add(l); + }); + + + } catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + + @Test + public void testOpenAPIDocs() + { + when().get("openapi.yaml").then().log().all().statusCode(200); + } + + public void testPojoType() + { + when().get("tests/types/pojo").then().log().all().statusCode(200); + } + + @Test public void testPojoType2() + { + when().get("tests/types/pojo2").then().log().all().statusCode(200); + } + + public void testMoneyType() + { + when().get("tests/types/money").then().log().all().statusCode(200); + } + + + @Test + public void testSecurityRequirementEndpoint() + { + when().get("tests/secure/resource").then().statusCode(200); + } + + + @After + public void tearDown() + { + try + { + if(file.exists()) + { + file.delete(); + } + + } catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/openapi/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java b/openapi/src/test/java/io/sinistral/proteus/test/controllers/Tests.java similarity index 95% rename from openapi/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java rename to openapi/src/test/java/io/sinistral/proteus/test/controllers/Tests.java index 71bf8dd..6616627 100644 --- a/openapi/src/test/java/io/sinistral/proteus/swagger/test/controllers/Tests.java +++ b/openapi/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 com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; @@ -16,6 +16,7 @@ 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.core.MediaType; @@ -127,8 +128,18 @@ 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(100.0,"USD") ).applicationJson(); + } + + @POST @Path("generic/set/bean") @Produces((MediaType.APPLICATION_JSON)) diff --git a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java b/openapi/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java similarity index 95% rename from openapi/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java rename to openapi/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java index 5d1ea93..fa501cf 100644 --- a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/DefaultServer.java +++ b/openapi/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java @@ -1,13 +1,13 @@ /** * */ -package io.sinistral.proteus.swagger.test.server; +package io.sinistral.proteus.test.server; import io.restassured.RestAssured; import io.sinistral.proteus.ProteusApplication; import io.sinistral.proteus.openapi.services.OpenAPIService; import io.sinistral.proteus.services.AssetsService; -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/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java b/openapi/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java similarity index 91% rename from openapi/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java rename to openapi/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java index c91cb8d..2f03a19 100644 --- a/openapi/src/test/java/io/sinistral/proteus/swagger/test/server/TestControllerEndpoints.java +++ b/openapi/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 io.restassured.RestAssured.when; @@ -66,6 +66,12 @@ public void testOpenAPIDocs() when().get("openapi.yaml").then().statusCode(200); } + @Test + public void testMoneyType() + { + when().get("tests/types/money").then().statusCode(200); + } + @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..ddd47d4 --- /dev/null +++ b/openapi/src/test/resources/application.conf @@ -0,0 +1,93 @@ + +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.modules.JacksonModule" + + tmpdir = ${java.io.tmpdir}/${application.name} + + # path to default favicon file + favicon = "/io/sinistral/proteus/favicon.ico" + +} + +api.version="v1" + +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..83ea4ff 100644 --- a/openapi/src/test/resources/logback-test.xml +++ b/openapi/src/test/resources/logback-test.xml @@ -16,11 +16,11 @@ - + - - - + + + 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/java/io/sinistral/proteus/test/controllers/Tests.java b/swagger/src/test/java/io/sinistral/proteus/test/controllers/Tests.java new file mode 100644 index 0000000..dc2b9aa --- /dev/null +++ b/swagger/src/test/java/io/sinistral/proteus/test/controllers/Tests.java @@ -0,0 +1,261 @@ +/** + * + */ +package io.sinistral.proteus.test.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import io.sinistral.proteus.annotations.Blocking; +import io.sinistral.proteus.server.ServerRequest; +import io.sinistral.proteus.server.ServerResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.undertow.server.HttpServerExchange; + +import javax.ws.rs.*; +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.concurrent.CompletableFuture; + +import static io.sinistral.proteus.server.ServerResponse.response; + +/** + * @author jbauer + * + */ + +@Api(tags="tests") +@Path("/tests") +@Produces((MediaType.APPLICATION_JSON)) +@Consumes((MediaType.MEDIA_TYPE_WILDCARD)) +@Singleton +public class Tests +{ + private static final ByteBuffer buffer; + static { + String message = "Hello, World!"; + byte[] messageBytes = message.getBytes(java.nio.charset.StandardCharsets.US_ASCII); + buffer = ByteBuffer.allocateDirect(messageBytes.length); + buffer.put(messageBytes); + buffer.flip(); + } + + @Inject + protected ObjectMapper objectMapper; + + + @GET + @Path("exchange/plaintext") + @Produces((MediaType.TEXT_PLAIN)) + @ApiOperation(value = "Plaintext endpoint" ) + public void exchangePlaintext(HttpServerExchange exchange) + { + response("Hello, World!").textPlain().send(exchange); + + } + + + @GET + @Path("response/plaintext") + @Produces((MediaType.TEXT_PLAIN)) + @ApiOperation(value = "Plaintext endpoint" ) + public ServerResponse responsePlaintext(ServerRequest request) + { + return response("Hello, World!").textPlain(); + + } + + @GET + @Path("response/future/map") + @ApiOperation(value = "Future map endpoint" ) + public CompletableFuture>> responseFutureMap( ServerRequest request ) + { + Map map = ImmutableMap.of("message", "success"); + return CompletableFuture.completedFuture(response( map ).applicationJson()); + } + + @GET + @Path("response/map") + @ApiOperation(value = "Map endpoint" ) + public ServerResponse> futureMap( ServerRequest request ) + { + Map map = ImmutableMap.of("message", "success"); + return response( map ).applicationJson(); + } + + @POST + @Path("response/file/path") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation(value = "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(); + } + + @POST + @Path("response/file/path/optional") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation(value = "Upload optional file path endpoint" ) + public ServerResponse responseUploadOptionalFilePath(ServerRequest request, @FormParam("file") Optional file ) throws Exception + { + if(file.isPresent()) + { + return response(ByteBuffer.wrap(Files.toByteArray(file.get().toFile()))).applicationOctetStream(); + } + else + { + return response().noContent(); + } + } + + @GET + @Path("generic/set") + @Produces((MediaType.APPLICATION_JSON)) + @ApiOperation(value = "Generic set endpoint" ) + public ServerResponse> genericSet( ServerRequest request, @QueryParam("ids") Set ids ) throws Exception + { + return response( ids ).applicationJson(); + } + + + @POST + @Path("generic/set/bean") + @Produces((MediaType.APPLICATION_JSON)) + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Generic bean set endpoint" ) + public ServerResponse> genericBeanSet( ServerRequest request, @BeanParam Set ids ) throws Exception + { + return response( ids ).applicationJson(); + } + + + @POST + @Path("generic/list/bean") + @Produces((MediaType.APPLICATION_JSON)) + @Consumes(MediaType.APPLICATION_JSON) + + @ApiOperation(value = "Generic bean list endpoint" ) + public ServerResponse> genericBeanList( ServerRequest request, @BeanParam List ids ) throws Exception + { + return response( ids ).applicationJson(); + } + + @GET + @Path("optional/set") + @Produces((MediaType.APPLICATION_JSON)) + @ApiOperation(value = "Generic optional set endpoint" ) + public ServerResponse> genericOptionalSet( ServerRequest request, @QueryParam("ids") Optional> ids ) throws Exception + { + return response( ids.get() ).applicationJson(); + } + + + @POST + @Path("response/parse/ids") + @Blocking + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Convert ids") + public ServerResponse> listConversion( ServerRequest request, @BeanParam List ids ) throws Exception + { + + return response( ids ).applicationJson(); + + + } + + @GET + @Path("response/parse/timestamp") + @Blocking + @Produces(MediaType.TEXT_PLAIN) + @ApiOperation(value = "Convert timestamp") + public ServerResponse timestampConversion( ServerRequest request, @QueryParam("timestamp") Timestamp timestamp ) throws Exception + { + return response().body(timestamp.toString()).textPlain(); + } + + @GET + @Path("response/parse/instant") + @Blocking + @Produces(MediaType.TEXT_PLAIN) + @ApiOperation(value = "Convert instant") + public ServerResponse instantConversion( ServerRequest request, @QueryParam("instant") Instant instant ) throws Exception + { + + return response().body(instant.toString()).textPlain(); + + + } + + @POST + @Path("response/bytebuffer") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes("*/*") + @ApiOperation(value = "Upload file path endpoint") + public ServerResponse responseUploadByteBuffer(ServerRequest request, @FormParam("file") ByteBuffer file ) throws Exception + { + + return response(file).applicationOctetStream(); + + + } + + @POST + @Path("response/file") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes("*/*") + @ApiOperation(value = "Upload file path endpoint") + public ServerResponse responseUploadFile(ServerRequest request, @FormParam("file") File file ) throws Exception + { + + ByteBuffer response = ByteBuffer.wrap(Files.asByteSource(file).read()); + + + return response(response).applicationOctetStream(); + + + } + + @GET + @Path("response/debug") + @ApiOperation(value = "Debug endpoint") + public ServerResponse> debugEndpoint(ServerRequest request) + { + try + { + Map map = ImmutableMap.of("message", "Hello, World!"); + + return response( map ).applicationJson(); + } catch(Exception e) + { + return response().badRequest(e); + } + } + + @GET + @Path("response/debug/blocking") + @Blocking + @ApiOperation(value="Debug blocking endpoint") + public ServerResponse> debugBlockingEndpoint(ServerRequest request) + { + try + { + Map map = ImmutableMap.of("message", "Hello, World!"); + + return response( map ).applicationJson(); + } catch(Exception e) + { + return response().badRequest(e); + } + } + +} diff --git a/swagger/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java b/swagger/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java new file mode 100644 index 0000000..af414eb --- /dev/null +++ b/swagger/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java @@ -0,0 +1,129 @@ +/** + * + */ +package io.sinistral.proteus.test.server; + +import io.restassured.RestAssured; +import io.sinistral.proteus.ProteusApplication; +import io.sinistral.proteus.services.AssetsService; +import io.sinistral.proteus.swagger.services.SwaggerService; +import io.sinistral.proteus.test.controllers.Tests; +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * @author jbauer + */ +public class DefaultServer extends BlockJUnit4ClassRunner +{ + private static Logger log = LoggerFactory.getLogger(DefaultServer.class.getCanonicalName()); + + private static boolean first = true; + + /** + * @param clazz + * @throws InitializationError + */ + public DefaultServer(Class clazz) throws InitializationError + { + super(clazz); + } + + @Override + public void run(final RunNotifier notifier) + { + notifier.addListener(new RunListener() + { + @Override + public void testStarted(Description description) throws Exception + { + + super.testStarted(description); + } + + @Override + public void testFinished(Description description) throws Exception + { + + super.testFinished(description); + } + }); + + runInternal(notifier); + + super.run(notifier); + } + + private static void runInternal(final RunNotifier notifier) + { + + if (first) + { + + first = false; + + final ProteusApplication app = new ProteusApplication(DefaultServer.class.getClassLoader().getResource("application.conf")); + + app.addService(SwaggerService.class); + app.addService(AssetsService.class); + + app.addController(Tests.class); + + app.start(); + + int port = 0; + + try + { + Thread.sleep(5000); + + System.out.println(app.getPorts()); + + List ports = app.getPorts(); + + port = ports.get(0); + + } catch (Exception e) + { + e.printStackTrace(); + } + + + + RestAssured.baseURI = String.format("http://localhost:%d/v1",port); + + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + + while (!app.isRunning()) + { + try + { + Thread.sleep(100L); + } catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + notifier.addListener(new RunListener() + { + @Override + public void testRunFinished(final Result result) throws Exception + { + app.shutdown(); + }; + }); + } + + } + +} diff --git a/swagger/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java b/swagger/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java new file mode 100644 index 0000000..4a4864f --- /dev/null +++ b/swagger/src/test/java/io/sinistral/proteus/test/server/TestControllerEndpoints.java @@ -0,0 +1,88 @@ +/** + * + */ +package io.sinistral.proteus.test.server; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.nio.file.Files; +import java.util.HashSet; +import java.util.Random; +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 io.restassured.http.ContentType; + +/* + * import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*; + */ +/** + * @author jbauer + */ +@RunWith(DefaultServer.class) +public class TestControllerEndpoints +{ + + private File file = null; + + private Set idSet = new HashSet<>(); + + + @Before + public void setUp() + { + try + { + byte[] bytes = new byte[8388608]; + Random random = new Random(); + random.nextBytes(bytes); + + file = Files.createTempFile("test-asset", ".mp4").toFile(); + + LongStream.range(1L,10L).forEach( l -> { + + idSet.add(l); + }); + + + } catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testSwaggerDocs() + { + given().accept(ContentType.JSON).when().get("swagger.json").then().statusCode(200).and().body("basePath", is("/v1")); + } + + + + @After + public void tearDown() + { + try + { + if(file.exists()) + { + file.delete(); + } + + } catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +}