Camel catalog - Add api to validate endpoint uri

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

Branch: refs/heads/master
Commit: 3e1097502bb06043b480969641a14f50a35f0259
Parents: 6a4534d
Author: Claus Ibsen <davscl...@apache.org>
Authored: Sat Dec 19 14:49:43 2015 +0100
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Sun Dec 20 07:05:47 2015 +0100

----------------------------------------------------------------------
 .../org/apache/camel/catalog/CamelCatalog.java  |  11 ++
 .../camel/catalog/DefaultCamelCatalog.java      | 117 +++++++++++++++++++
 .../apache/camel/catalog/JSonSchemaHelper.java  |  78 +++++++++++++
 .../apache/camel/catalog/CamelCatalogTest.java  |  45 ++++++-
 4 files changed, 250 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/3e109750/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
----------------------------------------------------------------------
diff --git 
a/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java 
b/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
index 7dc67a0..dc1a3ca 100644
--- a/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
+++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java
@@ -170,6 +170,17 @@ public interface CamelCatalog {
     Map<String, String> endpointProperties(String uri) throws 
URISyntaxException;
 
     /**
+     * Parses and validates the endpoint uri and constructs a key/value 
properties of each option
+     *
+     * @param uri  the endpoint uri
+     * @return invalid properties as key/value properties of each invalid 
option, returns an empty map if no validation errors
+     */
+    Map<String, String> validateProperties(String uri) throws 
URISyntaxException;
+
+    // TODO: json with error instead of map
+    // with description of the error, index, etc
+
+    /**
      * Returns the component name from the given endpoint uri
      *
      * @param uri  the endpoint uri

http://git-wip-us.apache.org/repos/asf/camel/blob/3e109750/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java
----------------------------------------------------------------------
diff --git 
a/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java
 
b/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java
index 870bc0f..13b3052 100644
--- 
a/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java
+++ 
b/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java
@@ -42,8 +42,14 @@ import org.w3c.dom.Document;
 
 import static org.apache.camel.catalog.CatalogHelper.after;
 import static 
org.apache.camel.catalog.JSonSchemaHelper.getPropertyDefaultValue;
+import static org.apache.camel.catalog.JSonSchemaHelper.getPropertyEnum;
+import static org.apache.camel.catalog.JSonSchemaHelper.getRow;
+import static org.apache.camel.catalog.JSonSchemaHelper.isPropertyBoolean;
+import static org.apache.camel.catalog.JSonSchemaHelper.isPropertyInteger;
+import static org.apache.camel.catalog.JSonSchemaHelper.isPropertyNumber;
 import static org.apache.camel.catalog.JSonSchemaHelper.isPropertyRequired;
 import static org.apache.camel.catalog.URISupport.createQueryString;
+import static org.apache.camel.catalog.URISupport.isEmpty;
 import static org.apache.camel.catalog.URISupport.normalizeUri;
 import static org.apache.camel.catalog.URISupport.stripQuery;
 
@@ -72,9 +78,17 @@ public class DefaultCamelCatalog implements CamelCatalog {
 
     private boolean caching;
 
+    /**
+     * Creates the {@link CamelCatalog} without caching enabled.
+     */
     public DefaultCamelCatalog() {
     }
 
+    /**
+     * Creates the {@link CamelCatalog}
+     *
+     * @param caching  whether to use cache
+     */
     public DefaultCamelCatalog(boolean caching) {
         this.caching = caching;
     }
@@ -632,6 +646,109 @@ public class DefaultCamelCatalog implements CamelCatalog {
     }
 
     @Override
+    public Map<String, String> validateProperties(String uri) throws 
URISyntaxException {
+        Map<String, String> answer = new LinkedHashMap<String, String>();
+
+        // parse the uri
+        URI u = normalizeUri(uri);
+        String scheme = u.getScheme();
+        String json = componentJSonSchema(scheme);
+        List<Map<String, String>> rows = 
JSonSchemaHelper.parseJsonSchema("properties", json, true);
+
+        // parse into a map of properties of the uri, and look for options 
that are invalid
+        Map<String, String> properties = endpointProperties(uri);
+        for (Map.Entry<String, String> property : properties.entrySet()) {
+            String name = property.getKey();
+            String value = property.getValue();
+            boolean placeholder = value.startsWith("{{") || 
value.startsWith("${") || value.startsWith("$simple{");
+
+            Map<String, String> row = getRow(rows, name);
+            // unknown option
+            if (row == null) {
+                answer.put(name, property.getValue());
+            } else {
+                // invalid value/type
+
+                // is required but the value is empty
+                boolean required = isPropertyRequired(rows, name);
+                if (required && isEmpty(value)) {
+                    answer.put(name, value);
+                }
+
+                // is enum but the value is not within the enum range
+                // but we can only check if the value is not a placeholder
+                String enums = getPropertyEnum(rows, name);
+                if (!placeholder && enums != null) {
+                    boolean found = false;
+                    for (String s : enums.split(",")) {
+                        if (value.equalsIgnoreCase(s)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        answer.put(name, value);
+                    }
+                }
+
+                // is boolean
+                if (!placeholder && isPropertyBoolean(rows, name)) {
+                    // value must be a boolean
+                    boolean bool = "true".equalsIgnoreCase(value) || 
"false".equalsIgnoreCase(value);
+                    if (!bool) {
+                        answer.put(name, value);
+                    }
+                }
+
+                // is integer
+                if (!placeholder && isPropertyInteger(rows, name)) {
+                    // value must be an integer
+                    boolean valid = false;
+                    try {
+                        valid = Integer.valueOf(value) != null;
+                    } catch (Exception e) {
+                        // ignore
+                    }
+                    if (!valid) {
+                        answer.put(name, value);
+                    }
+                }
+
+                // is number
+                if (!placeholder && isPropertyNumber(rows, name)) {
+                    // value must be an number
+                    boolean valid = false;
+                    try {
+                        valid = Double.valueOf(value).isNaN() == false || 
Float.valueOf(value).isNaN() == false;
+                    } catch (Exception e) {
+                        // ignore
+                    }
+                    if (!valid) {
+                        answer.put(name, value);
+                    }
+                }
+            }
+        }
+
+        // now check if all required values are there, and that a default 
value does not exists
+        for (Map<String, String> row : rows) {
+            String name = row.get("name");
+            boolean required = isPropertyRequired(rows, name);
+            if (required) {
+                String value = properties.get(name);
+                if (isEmpty(value)) {
+                    value = getPropertyDefaultValue(rows, name);
+                }
+                if (isEmpty(value)) {
+                    answer.put(name, value);
+                }
+            }
+        }
+
+        return answer;
+    }
+
+    @Override
     public Map<String, String> endpointProperties(String uri) throws 
URISyntaxException {
         // NOTICE: This logic is similar to 
org.apache.camel.util.EndpointHelper#endpointProperties
         // as the catalog also offers similar functionality (without having 
camel-core on classpath)

http://git-wip-us.apache.org/repos/asf/camel/blob/3e109750/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
----------------------------------------------------------------------
diff --git 
a/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
 
b/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
index 17e0f85..e3e4beb 100644
--- 
a/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
+++ 
b/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
@@ -129,6 +129,57 @@ public final class JSonSchemaHelper {
         return false;
     }
 
+    public static boolean isPropertyBoolean(List<Map<String, String>> rows, 
String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "boolean".equals(type);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPropertyInteger(List<Map<String, String>> rows, 
String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "integer".equals(type);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPropertyNumber(List<Map<String, String>> rows, 
String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "number".equals(type);
+            }
+        }
+        return false;
+    }
+
     public static String getPropertyDefaultValue(List<Map<String, String>> 
rows, String name) {
         for (Map<String, String> row : rows) {
             String defaultValue = null;
@@ -146,4 +197,31 @@ public final class JSonSchemaHelper {
         return null;
     }
 
+    public static String getPropertyEnum(List<Map<String, String>> rows, 
String name) {
+        for (Map<String, String> row : rows) {
+            String enums = null;
+            String defaultValue = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("enum")) {
+                enums = row.get("enum");
+            }
+            if (found) {
+                return enums;
+            }
+        }
+        return null;
+    }
+
+    public static Map<String, String> getRow(List<Map<String, String>> rows, 
String key) {
+        for (Map<String, String> row : rows) {
+            if (key.equals(row.get("name"))) {
+                return row;
+            }
+        }
+        return null;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/3e109750/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
----------------------------------------------------------------------
diff --git 
a/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
 
b/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index 324362f..4c020da 100644
--- 
a/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++ 
b/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -22,7 +22,6 @@ import java.util.Map;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -388,6 +387,50 @@ public class CamelCatalogTest {
     }
 
     @Test
+    public void validateProperties() throws Exception {
+        // valid
+        Map<String, String> map = catalog.validateProperties("log:mylog");
+        assertNotNull(map);
+        assertEquals(0, map.size());
+
+        // unknown
+        map = catalog.validateProperties("log:mylog?level=WARN&foo=bar");
+        assertNotNull(map);
+        assertEquals(1, map.size());
+        assertEquals("bar", map.get("foo"));
+
+        // enum
+        map = catalog.validateProperties("jms:unknown:myqueue");
+        assertNotNull(map);
+        assertEquals(1, map.size());
+
+        // okay
+        map = 
catalog.validateProperties("yammer:MESSAGES?accessToken=aaa&consumerKey=bbb&consumerSecret=ccc&useJson=true&initialDelay=500");
+        assertNotNull(map);
+        assertEquals(0, map.size());
+
+        // required / boolean / integer
+        map = 
catalog.validateProperties("yammer:MESSAGES?accessToken=aaa&consumerKey=&useJson=no&initialDelay=five");
+        assertNotNull(map);
+        assertEquals(4, map.size());
+        assertEquals(null, map.get("consumerKey"));
+        assertEquals(null, map.get("consumerSecret"));
+        assertEquals("no", map.get("useJson"));
+        assertEquals("five", map.get("initialDelay"));
+
+        // okay
+        map = 
catalog.validateProperties("mqtt:myqtt?reconnectBackOffMultiplier=2.5");
+        assertNotNull(map);
+        assertEquals(0, map.size());
+
+        // number
+        map = 
catalog.validateProperties("mqtt:myqtt?reconnectBackOffMultiplier=five");
+        assertNotNull(map);
+        assertEquals(1, map.size());
+        assertEquals("five", map.get("reconnectBackOffMultiplier"));
+    }
+
+    @Test
     public void testEndpointComponentName() throws Exception {
         String name = catalog.endpointComponentName("jms:queue:foo");
         assertEquals("jms", name);

Reply via email to