Skip to content

Commit

Permalink
Merge pull request microprofile#329 from phillip-kruger/master
Browse files Browse the repository at this point in the history
Baby steps: Adding support for PUT and DELETE in Spring
  • Loading branch information
phillip-kruger authored May 26, 2020
2 parents 7d7a107 + 001adbc commit 462c20f
Show file tree
Hide file tree
Showing 21 changed files with 813 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.ParameterizedType;
import org.jboss.jandex.Type;

import io.smallrye.openapi.api.constants.OpenApiConstants;
Expand Down Expand Up @@ -85,6 +86,8 @@ public interface AnnotationScanner {

public boolean isPostMethod(final MethodInfo method);

public boolean isDeleteMethod(final MethodInfo method);

public boolean isScannerInternalResponse(Type returnType);

public boolean isMultipartOutput(Type returnType);
Expand Down Expand Up @@ -347,7 +350,7 @@ default void createResponseFromRestMethod(final AnnotationScannerContext context
final String code = String.valueOf(status);
final String description = getReasonPhrase(status);

if (returnType.kind() == Type.Kind.VOID) {
if (isVoidResponse(method)) {
if (generateResponse(code, operation)) {
response = new APIResponseImpl().description(description);
}
Expand Down Expand Up @@ -417,9 +420,11 @@ default void createResponseFromRestMethod(final AnnotationScannerContext context
default int getDefaultStatus(final MethodInfo method) {
final int status;

if (method.returnType().kind() == Type.Kind.VOID) {
if (isVoidResponse(method)) {
if (isPostMethod(method)) {
status = 201; // Created
} else if (isDeleteMethod(method)) {
status = 204; // No Content (Maybe this should be 202 Accepted ?)
} else if (!isAsyncResponse(method)) {
status = 204; // No Content
} else {
Expand All @@ -432,6 +437,23 @@ default int getDefaultStatus(final MethodInfo method) {
return status;
}

default boolean isVoidResponse(final MethodInfo method) {
if (method.returnType().kind().equals(Type.Kind.VOID)) {
return true;
}
if (isWrapperType(method.returnType())) {
ParameterizedType parameterizedType = method.returnType().asParameterizedType();
List<Type> arguments = parameterizedType.arguments();
for (Type argument : arguments) {
if (argument.kind().equals(Type.Kind.VOID) || (argument.kind().equals(Type.Kind.CLASS)
&& argument.name().equals(DotName.createSimple(Void.class.getName())))) {
return true;
}
}
}
return false;
}

/**
* Determine if the default response information should be generated.
* It should be done when no responses have been declared or if the default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public boolean isPostMethod(final MethodInfo method) {
return method.hasAnnotation(JaxRsConstants.POST);
}

@Override
public boolean isDeleteMethod(final MethodInfo method) {
return method.hasAnnotation(JaxRsConstants.DELETE);
}

@Override
public boolean isScannerInternalResponse(Type returnType) {
return returnType.name().equals(JaxRsConstants.RESPONSE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
import java.io.IOException;

import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.jboss.jandex.Indexer;
import org.jboss.jandex.Index;
import org.json.JSONException;
import org.junit.Test;

import test.io.smallrye.openapi.runtime.scanner.entities.Greeting;
import test.io.smallrye.openapi.runtime.scanner.resources.GreetingDeleteResource;
import test.io.smallrye.openapi.runtime.scanner.resources.GreetingGetResource;
import test.io.smallrye.openapi.runtime.scanner.resources.GreetingPostResource;
import test.io.smallrye.openapi.runtime.scanner.resources.GreetingPutResource;

/**
* Basic tests mostly to compare with Spring
*
Expand All @@ -20,12 +26,10 @@ public class JaxRsAnnotationScannerBasicTest extends JaxRsDataObjectScannerTestB
* @throws IOException
* @throws JSONException
*/
//@Test
@Test
public void testBasicJaxRsGetDefinitionScanning() throws IOException, JSONException {
Indexer indexer = new Indexer();
index(indexer, "test/io/smallrye/openapi/runtime/scanner/resources/GreetingGetResource.class");
index(indexer, "test/io/smallrye/openapi/runtime/scanner/entities/Greeting.class");
OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), indexer.complete());
Index i = indexOf(GreetingGetResource.class, Greeting.class);
OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), i);

OpenAPI result = scanner.scan();

Expand All @@ -41,15 +45,46 @@ public void testBasicJaxRsGetDefinitionScanning() throws IOException, JSONExcept
*/
@Test
public void testBasicJaxRsPostDefinitionScanning() throws IOException, JSONException {
Indexer indexer = new Indexer();
index(indexer, "test/io/smallrye/openapi/runtime/scanner/resources/GreetingPostResource.class");
index(indexer, "test/io/smallrye/openapi/runtime/scanner/entities/Greeting.class");
OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), indexer.complete());
Index i = indexOf(GreetingPostResource.class, Greeting.class);
OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), i);

OpenAPI result = scanner.scan();

printToConsole(result);
assertJsonEquals("resource.testBasicJaxRsPostDefinitionScanning.json", result);
}

/**
* This test a basic, no OpenApi annotations, hello world PUT service
*
* @throws IOException
* @throws JSONException
*/
@Test
public void testBasicJaxRsPutDefinitionScanning() throws IOException, JSONException {
Index i = indexOf(GreetingPutResource.class, Greeting.class);
OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), i);

OpenAPI result = scanner.scan();

printToConsole(result);
assertJsonEquals("resource.testBasicJaxRsPutDefinitionScanning.json", result);
}

/**
* This test a basic, no OpenApi annotations, hello world DELETE service
*
* @throws IOException
* @throws JSONException
*/
@Test
public void testBasicJaxRsDeleteDefinitionScanning() throws IOException, JSONException {
Index i = indexOf(GreetingDeleteResource.class, Greeting.class);
OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), i);

OpenAPI result = scanner.scan();

printToConsole(result);
assertJsonEquals("resource.testBasicJaxRsDeleteDefinitionScanning.json", result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package test.io.smallrye.openapi.runtime.scanner.resources;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;

/**
* JAX-RS.
* Some basic tests, mostly to compare with the Spring implementation
*
* @author Phillip Kruger ([email protected])
*/
@Path("/greeting")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GreetingDeleteResource {

// 1) Basic path var test
@DELETE
@Path("/greet/{id}")
public void greet(@PathParam("id") String id) {

}

// 2) Response where you do not have a type.
@DELETE
@Path("/greetWithResponse/{id}")
@APIResponse(responseCode = "204", description = "No Content")
public Response greetWithResponse(@PathParam("id") String id) {
return Response.noContent().build();
}

// 3) Response with a type specified (No JaxRS comparison) (repeat of above as there is not wrapped type return
@DELETE
@Path("/greetWithResponseTyped/{id}")
@APIResponse(responseCode = "204", description = "No Content")
public Response greetWithResponseTyped(@PathParam("id") String id) {
return Response.noContent().build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ public Greeting helloRequestParam(@QueryParam("name") String name) {
// 5) Response where you do not have a type.
@GET
@Path("/helloPathVariableWithResponse/{name}")
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting"))) // TODO: Why is not working to just do implementation ?
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting")))
public Response helloPathVariableWithResponse(@PathParam("name") String name) {
return Response.ok(new Greeting("Hello " + name)).build();
}

// 6) ResponseEntity with a type specified (No JaxRS comparison) (repeat of above as there is not wrapped type return
@GET
@Path("/helloPathVariableWithResponseTyped/{name}")
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting"))) // TODO: Why is not working to just do implementation ?
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting")))
public Response helloPathVariableWithResponseTyped(@PathParam("name") String name) {
return Response.ok(new Greeting("Hello " + name)).build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,39 +31,18 @@ public Greeting greet(Greeting greeting) {
return greeting;
}

// 2) Basic path var that return a collection test
// @GET
// @Path("/hellosPathVariable/{name}")
// public List<Greeting> hellosPathVariable(@PathParam("name") String name) {
// return Arrays.asList(new Greeting("Hello " + name));
// }
//
// // 3) Basic path var with Optional test
// @GET
// @Path("/helloOptional/{name}")
// public Optional<Greeting> helloOptional(@PathParam("name") String name) {
// return Optional.of(new Greeting("Hello " + name));
// }
//
// // 4) Basic request param test
// @GET
// @Path("/helloRequestParam")
// public Greeting helloRequestParam(@QueryParam("name") String name) {
// return new Greeting("Hello " + name);
// }
//
// 5) Response where you do not have a type.
// 2) Response where you do not have a type.
@POST
@Path("/greetWithResponse")
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting"))) // TODO: Why is not working to just do implementation ?
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting")))
public Response greetWithResponse(Greeting greeting) {
return Response.ok(greeting).build();
}

// 6) Response with a type specified (No JaxRS comparison) (repeat of above as there is not wrapped type return
// 3) Response with a type specified (No JaxRS comparison) (repeat of above as there is not wrapped type return
@POST
@Path("/greetWithResponseTyped")
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting"))) // TODO: Why is not working to just do implementation ?
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting")))
public Response greetWithResponseTyped(Greeting greeting) {
return Response.ok(greeting).build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package test.io.smallrye.openapi.runtime.scanner.resources;

import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;

import test.io.smallrye.openapi.runtime.scanner.entities.Greeting;

/**
* JAX-RS.
* Some basic tests, mostly to compare with the Spring implementation
*
* @author Phillip Kruger ([email protected])
*/
@Path("/greeting")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GreetingPutResource {

// 1) Basic path var test
@PUT
@Path("/greet/{id}")
public Greeting greet(Greeting greeting, @PathParam("id") String id) {
return greeting;
}

// 2) Response where you do not have a type.
@PUT
@Path("/greetWithResponse/{id}")
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting")))
public Response greetWithResponse(Greeting greeting, @PathParam("id") String id) {
return Response.ok(greeting).build();
}

// 3) Response with a type specified (No JaxRS comparison) (repeat of above as there is not wrapped type return
@PUT
@Path("/greetWithResponseTyped/{id}")
@APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(ref = "#/components/schemas/Greeting")))
public Response greetWithResponseTyped(Greeting greeting, @PathParam("id") String id) {
return Response.ok(greeting).build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"openapi" : "3.0.1",
"paths" : {
"/greeting/greet/{id}" : {
"delete" : {
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"204" : {
"description" : "No Content"
}
}
}
},
"/greeting/greetWithResponse/{id}" : {
"delete" : {
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"204" : {
"description" : "No Content"
}
}
}
},
"/greeting/greetWithResponseTyped/{id}" : {
"delete" : {
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"204" : {
"description" : "No Content"
}
}
}
}
}
}
Loading

0 comments on commit 462c20f

Please sign in to comment.