[CXF-6571] Adding an option in Swagger2Feature to scan non-swagger annotated resources
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/3808f19a Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/3808f19a Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/3808f19a Branch: refs/heads/3.0.x-fixes Commit: 3808f19a80a639cfd80c054dc136fb897165da3c Parents: f564774 Author: Akitoshi Yoshida <[email protected]> Authored: Tue Sep 1 16:49:44 2015 +0200 Committer: Akitoshi Yoshida <[email protected]> Committed: Tue Sep 1 17:47:27 2015 +0200 ---------------------------------------------------------------------- .../cxf/jaxrs/swagger/Swagger2Feature.java | 54 +++++++++++++ .../AbstractSwagger2ServiceDescriptionTest.java | 20 +++-- .../Swagger2FilterServiceDescriptionTest.java | 10 +++ ...gger2NonAnnotatedServiceDescriptionTest.java | 82 ++++++++++++++++++++ .../Swagger2RegularServiceDescriptionTest.java | 10 +++ .../jaxrs/description/group1/BookStore.java | 82 ++++++++++++++++++++ .../jaxrs/description/swagger2-noano-json.txt | 1 + .../jaxrs/description/swagger2-noano-yaml.txt | 52 +++++++++++++ 8 files changed, 305 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java ---------------------------------------------------------------------- diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java index 659e91e..42f3950 100644 --- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java @@ -21,7 +21,9 @@ package org.apache.cxf.jaxrs.swagger; import java.io.IOException; import java.net.URI; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; @@ -30,6 +32,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.endpoint.Server; import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; import org.apache.cxf.jaxrs.ext.MessageContext; @@ -38,11 +41,15 @@ import org.apache.cxf.jaxrs.provider.ServerProviderFactory; import org.apache.cxf.jaxrs.utils.InjectionUtils; import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.config.DefaultReaderConfig; +import io.swagger.jaxrs.config.ReaderConfig; import io.swagger.jaxrs.listing.ApiListingResource; import io.swagger.jaxrs.listing.SwaggerSerializers; public class Swagger2Feature extends AbstractSwaggerFeature { private String host; + private boolean scanAllResources; + private String ignoreRoutes; @Override protected void addSwaggerResource(Server server) { @@ -64,6 +71,7 @@ public class Swagger2Feature extends AbstractSwaggerFeature { providers.add(new SwaggerContainerRequestFilter()); } providers.add(new SwaggerSerializers()); + providers.add(new ReaderConfigFilter()); ((ServerProviderFactory)server.getEndpoint().get( ServerProviderFactory.class.getName())).setUserProviders(providers); @@ -87,6 +95,22 @@ public class Swagger2Feature extends AbstractSwaggerFeature { this.host = host; } + public boolean isScanAllResources() { + return scanAllResources; + } + + public void setScanAllResources(boolean scanAllResources) { + this.scanAllResources = scanAllResources; + } + + public String getIgnoreRoutes() { + return ignoreRoutes; + } + + public void setIgnoreRoutes(String ignoreRoutes) { + this.ignoreRoutes = ignoreRoutes; + } + @Override protected void setBasePathByAddress(String address) { if (!address.startsWith("/")) { @@ -120,4 +144,34 @@ public class Swagger2Feature extends AbstractSwaggerFeature { } } } + + private class ReaderConfigFilter implements ContainerRequestFilter { + @Context + private MessageContext mc; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + if (mc.getServletContext().getAttribute(ReaderConfig.class.getName()) == null) { + if (mc.getServletConfig() != null + && Boolean.valueOf(mc.getServletConfig().getInitParameter("scan.all.resources"))) { + addReaderConfig(mc.getServletConfig().getInitParameter("ignore.routes")); + } else if (isScanAllResources()) { + addReaderConfig(getIgnoreRoutes()); + } + } + } + + private void addReaderConfig(String ignoreRoutesParam) { + DefaultReaderConfig rc = new DefaultReaderConfig(); + rc.setScanAllResources(true); + if (ignoreRoutesParam != null) { + Set<String> routes = new LinkedHashSet<String>(); + for (String route : StringUtils.split(ignoreRoutesParam, ",")) { + routes.add(route.trim()); + } + rc.setIgnoredRoutes(routes); + } + mc.getServletContext().setAttribute(ReaderConfig.class.getName(), rc); + } + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/AbstractSwagger2ServiceDescriptionTest.java ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/AbstractSwagger2ServiceDescriptionTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/AbstractSwagger2ServiceDescriptionTest.java index 5583b5f..3332f47 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/AbstractSwagger2ServiceDescriptionTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/AbstractSwagger2ServiceDescriptionTest.java @@ -47,8 +47,8 @@ public abstract class AbstractSwagger2ServiceDescriptionTest extends AbstractBus @Ignore public abstract static class Server extends AbstractBusTestServerBase { - private final String port; - private final boolean runAsFilter; + protected final String port; + protected final boolean runAsFilter; Server(final String port, final boolean runAsFilter) { this.port = port; @@ -88,16 +88,19 @@ public abstract class AbstractSwagger2ServiceDescriptionTest extends AbstractBus } protected abstract String getPort(); + + protected abstract String getExpectedFileJson(); + + protected abstract String getExpectedFileYaml(); @Test public void testApiListingIsProperlyReturnedJSON() throws Exception { final WebClient client = createWebClient("/swagger.json"); - try { final Response r = client.get(); assertEquals(Status.OK.getStatusCode(), r.getStatus()); JSONAssert.assertEquals( - getExpectedValue("swagger2-json.txt", getPort()), + getExpectedValue(getExpectedFileJson(), getPort()), IOUtils.readStringFromStream((InputStream)r.getEntity()), false); } finally { @@ -112,9 +115,14 @@ public abstract class AbstractSwagger2ServiceDescriptionTest extends AbstractBus try { final Response r = client.get(); assertEquals(Status.OK.getStatusCode(), r.getStatus()); + //REVISIT find a better way of reliably comparing two yaml instances. + // I noticed that yaml.load instantiates a Map and + // for an integer valued key, an Integer or a String is arbitrarily instantiated, + // which leads to the assertion error. So, we serilialize the yamls and compare the re-serialized texts. Yaml yaml = new Yaml(); - assertEquals(yaml.load(getExpectedValue("swagger2-yaml.txt", getPort())), - yaml.load(IOUtils.readStringFromStream((InputStream)r.getEntity()))); + assertEquals(yaml.load(getExpectedValue(getExpectedFileYaml(), getPort())).toString(), + yaml.load(IOUtils.readStringFromStream((InputStream)r.getEntity())).toString()); + } finally { client.close(); } http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2FilterServiceDescriptionTest.java ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2FilterServiceDescriptionTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2FilterServiceDescriptionTest.java index 142575f..2ca5455 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2FilterServiceDescriptionTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2FilterServiceDescriptionTest.java @@ -42,4 +42,14 @@ public class Swagger2FilterServiceDescriptionTest extends AbstractSwagger2Servic protected String getPort() { return PORT; } + + @Override + protected String getExpectedFileJson() { + return "swagger2-json.txt"; + } + + @Override + protected String getExpectedFileYaml() { + return "swagger2-yaml.txt"; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2NonAnnotatedServiceDescriptionTest.java ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2NonAnnotatedServiceDescriptionTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2NonAnnotatedServiceDescriptionTest.java new file mode 100644 index 0000000..f47af93 --- /dev/null +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2NonAnnotatedServiceDescriptionTest.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.systest.jaxrs.description; + +import java.util.Arrays; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; + +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; +import org.apache.cxf.jaxrs.swagger.Swagger2Feature; +import org.apache.cxf.systest.jaxrs.description.group1.BookStore; + +import org.junit.BeforeClass; + +public class Swagger2NonAnnotatedServiceDescriptionTest extends AbstractSwagger2ServiceDescriptionTest { + private static final String PORT = allocatePort(Swagger2NonAnnotatedServiceDescriptionTest.class); + + public static class SwaggerRegularNonAnnotated extends Server { + public SwaggerRegularNonAnnotated() { + super(PORT, false); + } + + @Override + protected void run() { + final JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); + sf.setResourceClasses(BookStore.class); + sf.setResourceProvider(BookStore.class, + new SingletonResourceProvider(new BookStore())); + sf.setProvider(new JacksonJsonProvider()); + final Swagger2Feature feature = new Swagger2Feature(); + feature.setRunAsFilter(runAsFilter); + //FIXME swagger-jaxrs 1.5.3 can't handle a self-recursive subresource like Book + // so we need to exclude "org.apache.cxf.systest.jaxrs" for now. + feature.setResourcePackage("org.apache.cxf.systest.jaxrs.description.group1"); + feature.setScanAllResources(true); + sf.setFeatures(Arrays.asList(feature)); + sf.setAddress("http://localhost:" + port + "/"); + sf.create(); + } + + public static void main(String[] args) { + start(new SwaggerRegularNonAnnotated()); + } + } + + @BeforeClass + public static void startServers() throws Exception { + startServers(SwaggerRegularNonAnnotated.class); + } + + @Override + protected String getPort() { + return PORT; + } + + @Override + protected String getExpectedFileJson() { + return "swagger2-noano-json.txt"; + } + + @Override + protected String getExpectedFileYaml() { + return "swagger2-noano-json.txt"; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2RegularServiceDescriptionTest.java ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2RegularServiceDescriptionTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2RegularServiceDescriptionTest.java index 25b255b..a95bfd6 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2RegularServiceDescriptionTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/Swagger2RegularServiceDescriptionTest.java @@ -42,4 +42,14 @@ public class Swagger2RegularServiceDescriptionTest extends AbstractSwagger2Servi protected String getPort() { return PORT; } + + @Override + protected String getExpectedFileJson() { + return "swagger2-json.txt"; + } + + @Override + protected String getExpectedFileYaml() { + return "swagger2-yaml.txt"; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group1/BookStore.java ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group1/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group1/BookStore.java new file mode 100644 index 0000000..f9bf4d7 --- /dev/null +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/group1/BookStore.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.systest.jaxrs.description.group1; + +import java.util.Arrays; + +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +//FIXME swagger-jaxrs 1.5.3 can't handle a self-recursive sub resource like Book. so hide Book for now +//import org.apache.cxf.systest.jaxrs.Book; + +@Path("/bookstore") +public class BookStore { +// @Produces({ MediaType.APPLICATION_JSON }) +// @GET +// public Response getBooks( +// @QueryParam("page") @DefaultValue("1") int page) { +// return Response.ok( +// Arrays.asList( +// new Book("Book 1", 1), +// new Book("Book 2", 2) +// ) +// ).build(); +// } +// +// @Produces({ MediaType.APPLICATION_JSON }) +// @Path("/{id}") +// @GET +// public Book getBook(@PathParam("id") Long id) { +// return new Book("Book", id); +// } + + @Produces({ MediaType.APPLICATION_JSON }) + @GET + @Path("/names") + public Response getBookNames( + @QueryParam("page") @DefaultValue("1") int page) { + return Response.ok( + Arrays.asList( + "Book 1", + "Book 2" + ) + ).build(); + } + + @Path("/name/{id}") + @GET + public String getBookName(@PathParam("id") String id) { + return "Book of " + id; + } + + @Path("/{id}") + @DELETE + public Response delete(@PathParam("id") String id) { + return Response.ok().build(); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-json.txt ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-json.txt b/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-json.txt new file mode 100644 index 0000000..75214ce --- /dev/null +++ b/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-json.txt @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"description":"The Application","version":"1.0.0","title":"Sample REST Application","contact":{"name":"[email protected]"},"license":{"name":"Apache 2.0 License","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}},"host":"localhost:%s","basePath":"/","paths":{"/bookstore/name/{id}":{"get":{"operationId":"getBookName","parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"type":"string"},"headers":{}}}}},"/bookstore/names":{"get":{"operationId":"getBookNames","parameters":[{"name":"page","in":"query","required":false,"type":"integer","default":"1","format":"int32"}],"responses":{"default":{"description":"successful operation"}}}},"/bookstore/{id}":{"delete":{"operationId":"delete","parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"default":{"description":"successful operation"}}}}}} http://git-wip-us.apache.org/repos/asf/cxf/blob/3808f19a/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-yaml.txt ---------------------------------------------------------------------- diff --git a/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-yaml.txt b/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-yaml.txt new file mode 100644 index 0000000..8bd33ee --- /dev/null +++ b/systests/jaxrs/src/test/resources/org/apache/cxf/systest/jaxrs/description/swagger2-noano-yaml.txt @@ -0,0 +1,52 @@ +--- +swagger: "2.0" +info: + description: "The Application" + version: "1.0.0" + title: "Sample REST Application" + contact: + name: "[email protected]" + license: + name: "Apache 2.0 License" + url: "http://www.apache.org/licenses/LICENSE-2.0.html" +host: "localhost:%s" +basePath: "/" +paths: + /bookstore/name/{id}: + get: + operationId: "getBookName" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + responses: + 200: + description: "successful operation" + schema: + type: "string" + headers: {} + /bookstore/names: + get: + operationId: "getBookNames" + parameters: + - name: "page" + in: "query" + required: false + type: "integer" + default: "1" + format: "int32" + responses: + default: + description: "successful operation" + /bookstore/{id}: + delete: + operationId: "delete" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + responses: + default: + description: "successful operation"
