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]>'].

Reply via email to