This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new ffc1633 CXF-7525: Add support for Swagger 2.0 (OpenApi Spec 3.0).
Initial integration implementation. Adding support for customizations (dynamic
base path)
ffc1633 is described below
commit ffc163362bfc4e6851d22a452efd3672dd43e35f
Author: reta <[email protected]>
AuthorDate: Thu Jan 18 10:06:19 2018 -0500
CXF-7525: Add support for Swagger 2.0 (OpenApi Spec 3.0). Initial
integration implementation. Adding support for customizations (dynamic base
path)
---
.../samples/jax_rs/description_openapi_v3/pom.xml | 2 +-
parent/pom.xml | 2 +-
.../jaxrs/openapi/OpenApiCustomizedResource.java | 71 ++++++++++++++++++++++
.../cxf/jaxrs/openapi/OpenApiCustomizer.java | 64 +++++++++++++++++++
.../apache/cxf/jaxrs/openapi/OpenApiFeature.java | 15 ++++-
.../cxf/jaxrs/openapi/parse/OpenApiParseUtils.java | 11 +++-
.../AbstractOpenApiServiceDescriptionTest.java | 28 +++++----
.../description/openapi/OpenApiCustomizerTest.java | 65 ++++++++++++++++++++
8 files changed, 241 insertions(+), 17 deletions(-)
diff --git
a/distribution/src/main/release/samples/jax_rs/description_openapi_v3/pom.xml
b/distribution/src/main/release/samples/jax_rs/description_openapi_v3/pom.xml
index 65f8a51..6210624 100644
---
a/distribution/src/main/release/samples/jax_rs/description_openapi_v3/pom.xml
+++
b/distribution/src/main/release/samples/jax_rs/description_openapi_v3/pom.xml
@@ -63,7 +63,7 @@
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
- <version>3.6.1</version>
+ <version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
diff --git a/parent/pom.xml b/parent/pom.xml
index b20b70a..3f8463b 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -161,7 +161,7 @@
<cxf.spring.mock>spring-test</cxf.spring.mock>
<cxf.swagger2.version>1.5.17</cxf.swagger2.version>
<cxf.swagger.v3.version>2.0.0-rc3</cxf.swagger.v3.version>
- <cxf.swagger.ui.version>3.6.1</cxf.swagger.ui.version>
+ <cxf.swagger.ui.version>3.9.0</cxf.swagger.ui.version>
<cxf.velocity.version>2.0</cxf.velocity.version>
<cxf.woodstox.core.version>5.0.3</cxf.woodstox.core.version>
<cxf.woodstox.stax2-api.version>3.1.4</cxf.woodstox.stax2-api.version>
diff --git
a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizedResource.java
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizedResource.java
new file mode 100644
index 0000000..e5abfd9
--- /dev/null
+++
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizedResource.java
@@ -0,0 +1,71 @@
+/**
+ * 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.jaxrs.openapi;
+
+import javax.servlet.ServletConfig;
+import javax.ws.rs.GET;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.integration.GenericOpenApiContext;
+import io.swagger.v3.oas.integration.OpenApiContextLocator;
+import io.swagger.v3.oas.integration.api.OpenAPIConfiguration;
+import io.swagger.v3.oas.integration.api.OpenApiContext;
+
+import static
io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.getContextIdFromServletConfig;
+
+public class OpenApiCustomizedResource extends OpenApiResource {
+ private final OpenApiCustomizer customizer;
+
+ public OpenApiCustomizedResource(final OpenApiCustomizer customizer) {
+ this.customizer = customizer;
+ }
+
+ @GET
+ @Produces({MediaType.APPLICATION_JSON, "application/yaml"})
+ @Operation(hidden = true)
+ public Response getOpenApi(@Context ServletConfig config, @Context
HttpHeaders headers,
+ @Context UriInfo uriInfo, @PathParam("type") String type) throws
Exception {
+
+ if (customizer != null) {
+ final OpenAPIConfiguration configuration =
customizer.customize(getOpenApiConfiguration());
+ setOpenApiConfiguration(configuration);
+
+ // By default, the OpenApiContext instance is cached. It means
that the configuration
+ // changes won't be taken into account (due to the deep copying
rather than reference
+ // passing). In order to reflect any changes which customization
may do, we have to
+ // update reader's configuration directly.
+ final String ctxId = getContextIdFromServletConfig(config);
+ final OpenApiContext ctx =
OpenApiContextLocator.getInstance().getOpenApiContext(ctxId);
+ if (ctx instanceof GenericOpenApiContext<?>) {
+
((GenericOpenApiContext<?>)ctx).getOpenApiReader().setConfiguration(configuration);
+ }
+ }
+
+ return super.getOpenApi(headers, uriInfo, type);
+ }
+}
diff --git
a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizer.java
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizer.java
new file mode 100644
index 0000000..166d81d
--- /dev/null
+++
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizer.java
@@ -0,0 +1,64 @@
+/**
+ * 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.jaxrs.openapi;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+
+import io.swagger.v3.oas.integration.api.OpenAPIConfiguration;
+import io.swagger.v3.oas.models.servers.Server;
+
+public class OpenApiCustomizer {
+ private boolean dynamicBasePath;
+
+ public OpenAPIConfiguration customize(OpenAPIConfiguration configuration) {
+ if (configuration == null) {
+ return configuration;
+ }
+
+ if (dynamicBasePath) {
+ final MessageContext ctx = createMessageContext();
+ final String url =
StringUtils.substringBeforeLast(ctx.getUriInfo().getRequestUri().toString(),
"/");
+
+ final Collection<Server> servers =
configuration.getOpenAPI().getServers();
+ if (servers == null || servers.stream().noneMatch(s ->
s.getUrl().equalsIgnoreCase(url))) {
+
configuration.getOpenAPI().setServers(Collections.singletonList(new
Server().url(url)));
+ }
+ }
+
+ return configuration;
+ }
+
+ private MessageContext createMessageContext() {
+ return JAXRSUtils.createContextValue(JAXRSUtils.getCurrentMessage(),
null, MessageContext.class);
+ }
+
+ public void setDynamicBasePath(boolean dynamicBasePath) {
+ this.dynamicBasePath = dynamicBasePath;
+ }
+
+ public boolean isDynamicBasePath() {
+ return dynamicBasePath;
+ }
+}
diff --git
a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
index ec0709f..936727b 100644
---
a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
+++
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
@@ -91,6 +91,7 @@ public class OpenApiFeature extends AbstractFeature
implements SwaggerUiSupport,
// Additional components
private Map<String, SecurityScheme> securityDefinitions;
+ private OpenApiCustomizer customizer;
// Allows to pass the configuration location, usually
openapi-configuration.json
// or openapi-configuration.yml file.
@@ -361,6 +362,14 @@ public class OpenApiFeature extends AbstractFeature
implements SwaggerUiSupport,
public void setSecurityDefinitions(Map<String, SecurityScheme>
securityDefinitions) {
this.securityDefinitions = securityDefinitions;
}
+
+ public OpenApiCustomizer getCustomizer() {
+ return customizer;
+ }
+
+ public void setCustomizer(OpenApiCustomizer customizer) {
+ this.customizer = customizer;
+ }
@Override
public String findSwaggerUiRoot() {
@@ -384,7 +393,7 @@ public class OpenApiFeature extends AbstractFeature
implements SwaggerUiSupport,
protected void registerOpenApiResources(JAXRSServiceFactoryBean sfb,
Set<String> packages,
OpenAPIConfiguration config) {
sfb.setResourceClassesFromBeans(Arrays.asList(
- new OpenApiResource()
+ createOpenApiResource()
.openApiConfiguration(config)
.configLocation(configLocation)
.resourcePackages(packages)));
@@ -527,4 +536,8 @@ public class OpenApiFeature extends AbstractFeature
implements SwaggerUiSupport,
return hasComponents ? Optional.of(components) : Optional.empty();
}
+
+ private OpenApiResource createOpenApiResource() {
+ return (customizer == null) ? new OpenApiResource() : new
OpenApiCustomizedResource(customizer);
+ }
}
diff --git
a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/parse/OpenApiParseUtils.java
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/parse/OpenApiParseUtils.java
index 7155c79..510a7d6 100644
---
a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/parse/OpenApiParseUtils.java
+++
b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/parse/OpenApiParseUtils.java
@@ -95,13 +95,20 @@ public final class OpenApiParseUtils {
public static UserApplication getUserApplicationFromJson(String json) {
return getUserApplicationFromJson(json, new ParseConfiguration());
}
- public static UserApplication getUserApplicationFromJson(String json,
-
ParseConfiguration cfg) {
+ public static UserApplication getUserApplicationFromJson(String json,
ParseConfiguration cfg) {
JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter();
Map<String, Object> map = reader.fromJson(json);
UserApplication app = new UserApplication();
app.setBasePath("/");
+
+ List<Map<String, Object>> servers =
CastUtils.cast((List<?>)map.get("servers"));
+ if (servers != null && !servers.isEmpty()) {
+ final String url = (String)servers.get(0).get("url");
+ if (url != null) {
+ app.setBasePath(url);
+ }
+ }
Map<String, List<UserOperation>> userOpsMap = new
LinkedHashMap<String, List<UserOperation>>();
Set<String> tags = new HashSet<>();
diff --git
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/AbstractOpenApiServiceDescriptionTest.java
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/AbstractOpenApiServiceDescriptionTest.java
index b60f243..e4a845b 100644
---
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/AbstractOpenApiServiceDescriptionTest.java
+++
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/AbstractOpenApiServiceDescriptionTest.java
@@ -117,7 +117,7 @@ public abstract class AbstractOpenApiServiceDescriptionTest
extends AbstractBusC
protected static void startServers(final Class< ? extends Server>
serverClass) throws Exception {
AbstractResourceInfo.clearAllMaps();
//keep out of process due to stack traces testing failures
- assertTrue("server did not launch correctly",
launchServer(serverClass, true));
+ assertTrue("server did not launch correctly",
launchServer(serverClass, false));
createStaticBus();
}
@@ -127,12 +127,14 @@ public abstract class
AbstractOpenApiServiceDescriptionTest extends AbstractBusC
doTestApiListingIsProperlyReturnedJSON(false);
}
protected void doTestApiListingIsProperlyReturnedJSON(boolean
useXForwarded) throws Exception {
-
doTestApiListingIsProperlyReturnedJSON(createWebClient("/openapi.json"),
- useXForwarded);
+ doTestApiListingIsProperlyReturnedJSON(useXForwarded, null);
+ }
+ protected void doTestApiListingIsProperlyReturnedJSON(boolean
useXForwarded, String basePath) throws Exception {
+
doTestApiListingIsProperlyReturnedJSON(createWebClient("/openapi.json"),
useXForwarded, basePath);
checkUiResource();
}
protected static void doTestApiListingIsProperlyReturnedJSON(final
WebClient client,
- boolean
useXForwarded) throws Exception {
+ boolean useXForwarded, String basePath) throws Exception {
if (useXForwarded) {
client.header("USE_XFORWARDED", true);
}
@@ -140,35 +142,37 @@ public abstract class
AbstractOpenApiServiceDescriptionTest extends AbstractBusC
String swaggerJson = client.get(String.class);
UserApplication ap =
OpenApiParseUtils.getUserApplicationFromJson(swaggerJson);
assertNotNull(ap);
- assertEquals(useXForwarded ? "/reverse" : "/", ap.getBasePath());
+
+ if (basePath == null) {
+ assertEquals(useXForwarded ? "/reverse" : "/",
ap.getBasePath());
+ } else {
+ assertEquals(basePath, ap.getBasePath());
+ }
List<UserResource> urs = ap.getResources();
assertNotNull(urs);
assertEquals(1, urs.size());
UserResource r = urs.get(0);
- String basePath = "";
- if (!"/".equals(r.getPath())) {
- basePath = r.getPath();
- }
+
Map<String, UserOperation> map = r.getOperationsAsMap();
assertEquals(3, map.size());
UserOperation getBooksOp = map.get("getBooks");
assertEquals(HttpMethod.GET, getBooksOp.getVerb());
- assertEquals("/bookstore", basePath + getBooksOp.getPath());
+ assertEquals("/bookstore", getBooksOp.getPath());
assertEquals(MediaType.APPLICATION_JSON, getBooksOp.getProduces());
List<Parameter> getBooksOpParams = getBooksOp.getParameters();
assertEquals(1, getBooksOpParams.size());
assertEquals(ParameterType.QUERY,
getBooksOpParams.get(0).getType());
UserOperation getBookOp = map.get("getBook");
assertEquals(HttpMethod.GET, getBookOp.getVerb());
- assertEquals("/bookstore/{id}", basePath + getBookOp.getPath());
+ assertEquals("/bookstore/{id}", getBookOp.getPath());
assertEquals(MediaType.APPLICATION_JSON, getBookOp.getProduces());
List<Parameter> getBookOpParams = getBookOp.getParameters();
assertEquals(1, getBookOpParams.size());
assertEquals(ParameterType.PATH, getBookOpParams.get(0).getType());
UserOperation deleteOp = map.get("delete");
assertEquals(HttpMethod.DELETE, deleteOp.getVerb());
- assertEquals("/bookstore/{id}", basePath + deleteOp.getPath());
+ assertEquals("/bookstore/{id}", deleteOp.getPath());
List<Parameter> delOpParams = deleteOp.getParameters();
assertEquals(1, delOpParams.size());
assertEquals(ParameterType.PATH, delOpParams.get(0).getType());
diff --git
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/OpenApiCustomizerTest.java
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/OpenApiCustomizerTest.java
new file mode 100644
index 0000000..26b9f7d
--- /dev/null
+++
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/openapi/OpenApiCustomizerTest.java
@@ -0,0 +1,65 @@
+/**
+ * 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.openapi;
+
+import org.apache.cxf.jaxrs.openapi.OpenApiCustomizer;
+import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class OpenApiCustomizerTest extends
AbstractOpenApiServiceDescriptionTest {
+ private static final String PORT =
allocatePort(OpenApiCustomizerTest.class);
+
+ public static class OpenApiRegular extends Server {
+ public OpenApiRegular() {
+ super(PORT, false);
+ }
+
+ public static void main(String[] args) {
+ start(new OpenApiRegular());
+ }
+
+ @Override
+ protected OpenApiFeature createOpenApiFeature() {
+ final OpenApiCustomizer customizer = new OpenApiCustomizer();
+ customizer.setDynamicBasePath(true);
+
+ final OpenApiFeature feature = super.createOpenApiFeature();
+ feature.setCustomizer(customizer);
+
+ return feature;
+ }
+ }
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ startServers(OpenApiRegular.class);
+ }
+
+ @Override
+ protected String getPort() {
+ return PORT;
+ }
+
+ @Test
+ public void testApiListingIsProperlyReturnedJSON() throws Exception {
+ doTestApiListingIsProperlyReturnedJSON(false, "http://localhost:" +
getPort());
+ }
+}
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].