CAMEL-10164: swagger component for making rest calls with swagger schema 
validation and facade to actual HTTP client in use


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/b05fd502
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/b05fd502
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/b05fd502

Branch: refs/heads/master
Commit: b05fd50222b99d80920938bf99d02d670c6885d6
Parents: 9801df7
Author: Claus Ibsen <[email protected]>
Authored: Fri Aug 26 15:53:00 2016 +0200
Committer: Claus Ibsen <[email protected]>
Committed: Fri Aug 26 16:53:31 2016 +0200

----------------------------------------------------------------------
 .../camel/component/rest/RestEndpoint.java      | 17 ++++-
 .../producer/JettyRestProducerApiDocTest.java   | 51 +++++++++++++
 .../JettyRestProducerInvalidApiDocTest.java     | 73 ++++++++++++++++++
 .../swagger/SwaggerRestProducerFactory.java     | 78 ++++++++------------
 .../producer/DummyRestProducerFactory.java      |  6 +-
 5 files changed, 169 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/b05fd502/camel-core/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/component/rest/RestEndpoint.java 
b/camel-core/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
index 064924f..671e27f 100644
--- a/camel-core/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
+++ b/camel-core/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
@@ -252,6 +252,7 @@ public class RestEndpoint extends DefaultEndpoint {
 
     @Override
     public Producer createProducer() throws Exception {
+        RestProducerFactory apiDocFactory = null;
         RestProducerFactory factory = null;
 
         if (apiDoc != null) {
@@ -262,7 +263,7 @@ public class RestEndpoint extends DefaultEndpoint {
                 Object instance = 
finder.newInstance(DEFAULT_API_COMPONENT_NAME);
                 if (instance instanceof RestProducerFactory) {
                     // this factory from camel-swagger-java will facade the 
http component in use
-                    factory = (RestProducerFactory) instance;
+                    apiDocFactory = (RestProducerFactory) instance;
                 }
                 parameters.put("apiDoc", apiDoc);
             } catch (NoFactoryAvailableException e) {
@@ -270,8 +271,8 @@ public class RestEndpoint extends DefaultEndpoint {
             }
         }
 
-        String cname = null;
-        if (factory == null && getComponentName() != null) {
+        String cname = getComponentName();
+        if (cname != null) {
             Object comp = 
getCamelContext().getRegistry().lookupByName(getComponentName());
             if (comp != null && comp instanceof RestProducerFactory) {
                 factory = (RestProducerFactory) comp;
@@ -316,7 +317,15 @@ public class RestEndpoint extends DefaultEndpoint {
 
         if (factory != null) {
             LOG.debug("Using RestProducerFactory: {}", factory);
-            Producer producer = factory.createProducer(getCamelContext(), 
host, method, path, uriTemplate, queryParameters, consumes, produces, 
parameters);
+
+            Producer producer;
+            if (apiDocFactory != null) {
+                // wrap the factory using the api doc factory which will use 
the factory
+                parameters.put("restProducerFactory", factory);
+                producer = apiDocFactory.createProducer(getCamelContext(), 
host, method, path, uriTemplate, queryParameters, consumes, produces, 
parameters);
+            } else {
+                producer = factory.createProducer(getCamelContext(), host, 
method, path, uriTemplate, queryParameters, consumes, produces, parameters);
+            }
             return new RestProducer(this, producer);
         } else {
             throw new IllegalStateException("Cannot find RestProducerFactory 
in Registry or as a Component to use");

http://git-wip-us.apache.org/repos/asf/camel/blob/b05fd502/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerApiDocTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerApiDocTest.java
 
b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerApiDocTest.java
new file mode 100644
index 0000000..ede12e5
--- /dev/null
+++ 
b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerApiDocTest.java
@@ -0,0 +1,51 @@
+/**
+ * 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.camel.component.jetty.rest.producer;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jetty.BaseJettyTest;
+import org.junit.Test;
+
+public class JettyRestProducerApiDocTest extends BaseJettyTest {
+
+    @Test
+    public void testJettyProducerGet() throws Exception {
+        String out = fluentTemplate.withHeader("name", "Donald 
Duck").to("direct:start").request(String.class);
+        assertEquals("Hello Donald Duck", out);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use localhost with the given port
+                
restConfiguration().component("jetty").host("localhost").port(getPort());
+
+                from("direct:start")
+                        
.to("rest:get:api/hello/hi/{name}?apiDoc=hello-api.json");
+
+                // use the rest DSL to define the rest services
+                rest("/api/")
+                        .get("hello/hi/{name}")
+                        .route()
+                        .transform().simple("Hello ${header.name}");
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b05fd502/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerInvalidApiDocTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerInvalidApiDocTest.java
 
b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerInvalidApiDocTest.java
new file mode 100644
index 0000000..9711dab
--- /dev/null
+++ 
b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/producer/JettyRestProducerInvalidApiDocTest.java
@@ -0,0 +1,73 @@
+/**
+ * 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.camel.component.jetty.rest.producer;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JettyRestProducerInvalidApiDocTest extends CamelTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testInvalidPath() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use localhost with the given port
+                restConfiguration().component("jetty").host("localhost");
+
+                from("direct:start")
+                        
.to("rest:get:api/hello/unknown/{name}?apiDoc=hello-api.json");
+
+            }
+        });
+        try {
+            context.start();
+            fail("Should fail");
+        } catch (Exception e) {
+            IllegalArgumentException iae = 
assertIsInstanceOf(IllegalArgumentException.class, e.getCause().getCause());
+            assertEquals("Swagger api-doc does not contain operation for 
get:/api/hello/unknown/{name}", iae.getMessage());
+        }
+    }
+
+    @Test
+    public void testInvalidQuery() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use localhost with the given port
+                restConfiguration().component("jetty").host("localhost");
+
+                from("direct:start")
+                        
.to("rest:get:api/bye/?unknown={name}&apiDoc=hello-api.json");
+
+            }
+        });
+        try {
+            context.start();
+            fail("Should fail");
+        } catch (Exception e) {
+            IllegalArgumentException iae = 
assertIsInstanceOf(IllegalArgumentException.class, e.getCause().getCause());
+            assertEquals("Swagger api-doc does not contain query parameter 
name for get:/api/bye", iae.getMessage());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b05fd502/components/camel-swagger-java/src/main/java/org/apache/camel/swagger/SwaggerRestProducerFactory.java
----------------------------------------------------------------------
diff --git 
a/components/camel-swagger-java/src/main/java/org/apache/camel/swagger/SwaggerRestProducerFactory.java
 
b/components/camel-swagger-java/src/main/java/org/apache/camel/swagger/SwaggerRestProducerFactory.java
index 3a74778..7a7a6a4 100644
--- 
a/components/camel-swagger-java/src/main/java/org/apache/camel/swagger/SwaggerRestProducerFactory.java
+++ 
b/components/camel-swagger-java/src/main/java/org/apache/camel/swagger/SwaggerRestProducerFactory.java
@@ -19,15 +19,13 @@ package org.apache.camel.swagger;
 import java.io.InputStream;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import io.swagger.models.Operation;
 import io.swagger.models.Path;
 import io.swagger.models.Swagger;
+import io.swagger.models.parameters.Parameter;
 import io.swagger.parser.SwaggerParser;
 import org.apache.camel.CamelContext;
-import org.apache.camel.Component;
-import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.Producer;
 import org.apache.camel.spi.RestProducerFactory;
 import org.apache.camel.util.CollectionStringBuffer;
@@ -43,7 +41,7 @@ public class SwaggerRestProducerFactory implements 
RestProducerFactory {
 
     @Override
     public Producer createProducer(CamelContext camelContext, String host,
-                                   String verb, String basePath, String 
uriTemplate,
+                                   String verb, String basePath, String 
uriTemplate, String queryParameters,
                                    String consumes, String produces, 
Map<String, Object> parameters) throws Exception {
 
         String apiDoc = (String) parameters.get("apiDoc");
@@ -64,9 +62,25 @@ public class SwaggerRestProducerFactory implements 
RestProducerFactory {
             throw new IllegalArgumentException("Swagger api-doc does not 
contain operation for " + verb + ":" + path);
         }
 
+        // validate if we have the query parameters also
+        if (queryParameters != null) {
+            for (Parameter param : operation.getParameters()) {
+                if ("query".equals(param.getIn()) && param.getRequired()) {
+                    // check if we have the required query parameter defined
+                    String key = param.getName();
+                    String token = key + "=";
+                    boolean hasQuery = queryParameters.contains(token);
+                    if (!hasQuery) {
+                        throw new IllegalArgumentException("Swagger api-doc 
does not contain query parameter " + key + " for " + verb + ":" + path);
+                    }
+                }
+            }
+        }
+
         String componentName = (String) parameters.get("componentName");
 
-        Producer producer = createHttpProducer(camelContext, swagger, 
operation, host, verb, path, produces, consumes, componentName, parameters);
+        Producer producer = createHttpProducer(camelContext, swagger, 
operation, host, verb, path, queryParameters,
+                produces, consumes, componentName, parameters);
         return producer;
     }
 
@@ -83,6 +97,12 @@ public class SwaggerRestProducerFactory implements 
RestProducerFactory {
     }
 
     private Operation getSwaggerOperation(Swagger swagger, String verb, String 
path) {
+        // path may include base path so skip that
+        String basePath = swagger.getBasePath();
+        if (basePath != null && path.startsWith(basePath)) {
+            path = path.substring(basePath.length());
+        }
+
         Path modelPath = swagger.getPath(path);
         if (modelPath == null) {
             return null;
@@ -109,53 +129,13 @@ public class SwaggerRestProducerFactory implements 
RestProducerFactory {
     }
 
     private Producer createHttpProducer(CamelContext camelContext, Swagger 
swagger, Operation operation,
-                                        String host, String verb, String path, 
String consumes, String produces,
+                                        String host, String verb, String path, 
String queryParameters,
+                                        String consumes, String produces,
                                         String componentName, Map<String, 
Object> parameters) throws Exception {
 
         LOG.debug("Using Swagger operation: {} with {} {}", operation, verb, 
path);
 
-        RestProducerFactory factory = null;
-        String cname = null;
-        if (componentName != null) {
-            Object comp = 
camelContext.getRegistry().lookupByName(componentName);
-            if (comp != null && comp instanceof RestProducerFactory) {
-                factory = (RestProducerFactory) comp;
-            } else {
-                comp = camelContext.getComponent(componentName);
-                if (comp != null && comp instanceof RestProducerFactory) {
-                    factory = (RestProducerFactory) comp;
-                }
-            }
-
-            if (factory == null) {
-                if (comp != null) {
-                    throw new IllegalArgumentException("Component " + 
componentName + " is not a RestProducerFactory");
-                } else {
-                    throw new NoSuchBeanException(componentName, 
RestProducerFactory.class.getName());
-                }
-            }
-            cname = componentName;
-        }
-
-        // try all components
-        if (factory == null) {
-            for (String name : camelContext.getComponentNames()) {
-                Component comp = camelContext.getComponent(name);
-                if (comp != null && comp instanceof RestProducerFactory) {
-                    factory = (RestProducerFactory) comp;
-                    cname = name;
-                    break;
-                }
-            }
-        }
-
-        // lookup in registry
-        if (factory == null) {
-            Set<RestProducerFactory> factories = 
camelContext.getRegistry().findByType(RestProducerFactory.class);
-            if (factories != null && factories.size() == 1) {
-                factory = factories.iterator().next();
-            }
-        }
+        RestProducerFactory factory = (RestProducerFactory) 
parameters.remove("restProducerFactory");
 
         if (factory != null) {
             LOG.debug("Using RestProducerFactory: {}", factory);
@@ -200,7 +180,7 @@ public class SwaggerRestProducerFactory implements 
RestProducerFactory {
                 uriTemplate = null;
             }
 
-            return factory.createProducer(camelContext, host, verb, basePath, 
uriTemplate, consumes, produces, parameters);
+            return factory.createProducer(camelContext, host, verb, basePath, 
uriTemplate, queryParameters, consumes, produces, parameters);
 
         } else {
             throw new IllegalStateException("Cannot find RestProducerFactory 
in Registry or as a Component to use");

http://git-wip-us.apache.org/repos/asf/camel/blob/b05fd502/components/camel-swagger-java/src/test/java/org/apache/camel/swagger/producer/DummyRestProducerFactory.java
----------------------------------------------------------------------
diff --git 
a/components/camel-swagger-java/src/test/java/org/apache/camel/swagger/producer/DummyRestProducerFactory.java
 
b/components/camel-swagger-java/src/test/java/org/apache/camel/swagger/producer/DummyRestProducerFactory.java
index cd3903f..eddcbd5 100644
--- 
a/components/camel-swagger-java/src/test/java/org/apache/camel/swagger/producer/DummyRestProducerFactory.java
+++ 
b/components/camel-swagger-java/src/test/java/org/apache/camel/swagger/producer/DummyRestProducerFactory.java
@@ -30,7 +30,7 @@ public class DummyRestProducerFactory implements 
RestProducerFactory {
 
     @Override
     public Producer createProducer(CamelContext camelContext, String host,
-                            String verb, String basePath, final String 
uriTemplate,
+                            String verb, String basePath, final String 
uriTemplate, String queryParameters,
                             String consumes, String produces, Map<String, 
Object> parameters) throws Exception {
 
         // use a dummy endpoint
@@ -39,12 +39,12 @@ public class DummyRestProducerFactory implements 
RestProducerFactory {
         return new DefaultProducer(endpoint) {
             @Override
             public void process(Exchange exchange) throws Exception {
-                String query = exchange.getIn().getHeader(Exchange.HTTP_QUERY, 
String.class);
+                String query = 
exchange.getIn().getHeader(Exchange.REST_HTTP_QUERY, String.class);
                 if (query != null) {
                     String name = ObjectHelper.after(query, "name=");
                     exchange.getIn().setBody("Bye " + name);
                 }
-                String uri = exchange.getIn().getHeader(Exchange.HTTP_URI, 
String.class);
+                String uri = 
exchange.getIn().getHeader(Exchange.REST_HTTP_URI, String.class);
                 if (uri != null) {
                     int pos = uri.lastIndexOf('/');
                     String name = uri.substring(pos + 1);

Reply via email to