You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by zr...@apache.org on 2017/04/27 09:05:35 UTC

camel git commit: CAMEL-11203 Verifier should support exclusion o...

Repository: camel
Updated Branches:
  refs/heads/master c00d95352 -> d1177cd92


CAMEL-11203 Verifier should support exclusion o...

...f properties in option groups

This adds an exclusion modifier to the parameter given in an option
group. If the parameter name is preceded with `!` then it should not be
present when evaluating if the group is valid.


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

Branch: refs/heads/master
Commit: d1177cd926ee28cedd3c6462a8db758bc8cb1fb0
Parents: c00d953
Author: Zoran Regvart <zr...@apache.org>
Authored: Wed Apr 26 13:38:42 2017 +0200
Committer: Zoran Regvart <zr...@apache.org>
Committed: Thu Apr 27 11:05:19 2017 +0200

----------------------------------------------------------------------
 .../camel/impl/verifier/OptionsGroup.java       | 66 +++++++++++++
 .../camel/impl/verifier/ResultErrorHelper.java  | 97 ++++++++++++++++++--
 .../impl/verifier/ResultErrorHelperTest.java    | 76 +++++++++++++++
 .../salesforce/SalesforceComponentVerifier.java | 14 +--
 4 files changed, 236 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/d1177cd9/camel-core/src/main/java/org/apache/camel/impl/verifier/OptionsGroup.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/verifier/OptionsGroup.java b/camel-core/src/main/java/org/apache/camel/impl/verifier/OptionsGroup.java
index 6f9d633..1df4383 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/verifier/OptionsGroup.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/verifier/OptionsGroup.java
@@ -23,37 +23,85 @@ import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
+/**
+ * A named group of options. A group of options requires that a set of
+ * component parameters is given as a whole.
+ *
+ * <a id="#syntax">The option syntax can be
+ * {@code "propertyName"} to denote required property and
+ * {@code "!propertyName"} to denote required absence of a property.
+ */
 public final class OptionsGroup implements Serializable {
     private final String name;
     private final Set<String> options;
 
+    /**
+     * Creates new named {@link OptionsGroup}.
+     *
+     * @param name the name of the group
+     */
     public OptionsGroup(String name) {
         this.name = name;
         this.options = new HashSet<>();
     }
 
+    /**
+     * Creates new named {@link OptionsGroup} with a set of option
+     * definitions.
+     *
+     * @param name the name of the group
+     * @param options names of properties in the syntax mentioned in {@link OptionsGroup}
+     */
     public OptionsGroup(String name, Collection<String> options) {
         this.name = name;
         this.options = new LinkedHashSet<>(options);
     }
 
+    /**
+     * Adds a option definition to this group. The option syntax can be
+     * {@code "propertyName"} to denote required property and
+     * {@code "!propertyName"} to denote required absence of a property.
+     *
+     * @param option definition.
+     */
     public void addOption(String option) {
         this.options.add(option);
     }
 
+    /**
+     * The name of the group.
+     */
     public String getName() {
         return name;
     }
 
+    /**
+     * The option definitions in this group.
+     */
     public Set<String> getOptions() {
         return this.options;
     }
 
+    /**
+     * Adds a option definition to this group. The option syntax can be
+     * {@code "propertyName"} to denote required property and
+     * {@code "!propertyName"} to denote required absence of a property.
+     *
+     * @param option definition.
+     */
     public OptionsGroup option(String option) {
         this.options.add(option);
         return this;
     }
 
+    /**
+     * Adds a number of option definitions to this group. The option
+     * syntax can be {@code "propertyName"} to denote required
+     * property and {@code "!propertyName"} to denote required absence
+     * of a property.
+     *
+     * @param options options definition
+     */
     public OptionsGroup options(String... options) {
         for (String option : options) {
             addOption(option);
@@ -62,14 +110,32 @@ public final class OptionsGroup implements Serializable {
         return this;
     }
 
+    /**
+     * Creates new group with the specified name.
+     *
+     * @param name the name of the group
+     */
     public static OptionsGroup withName(String name) {
         return new OptionsGroup(name);
     }
 
+    /**
+     * Creates new group with the specified name of the given
+     * {@link Enum} name.
+     *
+     * @param enumItem the name of the group
+     * @see Enum#name()
+     */
     public static OptionsGroup withName(Enum<?> enumItem) {
         return new OptionsGroup(enumItem.name());
     }
 
+    /**
+     * Creates new group with the specified name and option definitions.
+     *
+     * @param name the name of the group
+     * @param options options definition 
+     */
     public static OptionsGroup withNameAndOptions(String name, String... options) {
         return new OptionsGroup(name, Arrays.asList(options));
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/d1177cd9/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorHelper.java b/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorHelper.java
index c8d7785..fb0dfc0 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorHelper.java
@@ -25,10 +25,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.camel.ComponentVerifier.VerificationError;
 import org.apache.camel.util.ObjectHelper;
 
+/**
+ * Helper that validates component parameters.
+ */
 public final class ResultErrorHelper {
 
     private ResultErrorHelper() {
@@ -46,42 +51,114 @@ public final class ResultErrorHelper {
      */
     public static Optional<VerificationError> requiresOption(String parameterName, Map<String, Object> parameters) {
         if (ObjectHelper.isEmpty(parameters.get(parameterName))) {
-            return Optional.of(
-                ResultErrorBuilder.withMissingOption(parameterName).build()
-            );
+            return Optional.of(ResultErrorBuilder.withMissingOption(parameterName).build());
         }
 
         return Optional.empty();
     }
 
+    /**
+     * Validates that the given parameters satisfy any grouped options
+     * ({@link OptionsGroup}). A parameter set is valid if it is
+     * present and required by least one of the groups.
+     *
+     * <p>As an example consider that there are two option groups that
+     * can be specified:
+     * <ul>
+     * <li>optionA: requires param1 and param2
+     * <li>optionB: requires param1 and param3
+     * </ul>
+     *
+     * Valid parameters are those that include param1 and either param2
+     * and/or param3.
+     *
+     * <p>Note the special syntax of {@link OptionsGroup#getOptions()}
+     * that can require an property ({@code "propertyName"}) or can
+     * forbid the presence of a property ({@code "!propertyName"}).
+     *
+     * <p>With that if in the example above if param2 is specified
+     * specifying param3 is not allowed, and vice versa option groups
+     * should be defined with options:
+     * <ul>
+     * <li>optionA: ["param1", "param2", "!param3"]
+     * <li>optionB: ["param1", "!param2", "param3"]
+     * </ul>
+     *
+     * @param parameters given parameters of a component
+     * @param groups groups of options
+     * @see OptionsGroup
+     */
     public static List<VerificationError> requiresAny(Map<String, Object> parameters, OptionsGroup... groups) {
         return requiresAny(parameters, Arrays.asList(groups));
     }
 
+    /**
+     * Validates that the given parameters satisfy any grouped options
+     * ({@link OptionsGroup}). A parameter set is valid if it is
+     * present and required by least one of the groups.
+     *
+     * @param parameters given parameters of a component
+     * @param groups groups of options
+     * @see #requiresAny(Map, OptionsGroup...)
+     * @see OptionsGroup
+     */
     public static List<VerificationError> requiresAny(Map<String, Object> parameters, Collection<OptionsGroup> groups) {
         final List<VerificationError> verificationErrors = new ArrayList<>();
         final Set<String> keys = new HashSet<>(parameters.keySet());
 
         for (OptionsGroup group : groups) {
-            if (keys.containsAll(group.getOptions())) {
+            final Set<String> required = required(group.getOptions());
+            final Set<String> excluded = excluded(group.getOptions());
+
+            final ResultErrorBuilder builder = new ResultErrorBuilder()
+                .code(VerificationError.StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION)
+                .detail(VerificationError.GroupAttribute.GROUP_NAME, group.getName())
+                .detail(VerificationError.GroupAttribute.GROUP_OPTIONS, String.join(",", parameters(group.getOptions())));
+
+            if (keys.containsAll(required)) {
                 // All the options of this group are found so we are good
-                return Collections.emptyList();
+                final Set<String> shouldBeExcluded = new HashSet<>(keys);
+                shouldBeExcluded.retainAll(excluded);
+
+                if (shouldBeExcluded.isEmpty()) {
+                    // None of the excluded properties is present, also good
+                    return Collections.emptyList();
+                }
+
+                shouldBeExcluded.forEach(builder::parameterKey);
+                verificationErrors.add(builder.build());
             } else {
-                ResultErrorBuilder builder = new ResultErrorBuilder()
-                    .code(VerificationError.StandardCode.INCOMPLETE_PARAMETER_GROUP)
-                    .detail(VerificationError.GroupAttribute.GROUP_NAME, group.getName())
-                    .detail(VerificationError.GroupAttribute.GROUP_OPTIONS, String.join(",", group.getOptions()));
 
-                for (String option : group.getOptions()) {
+                for (String option : required) {
                     if (!parameters.containsKey(option)) {
                         builder.parameterKey(option);
                     }
                 }
 
+                for (String option : excluded) {
+                    if (parameters.containsKey(option)) {
+                        builder.parameterKey(option);
+                    }
+                }
+
                 verificationErrors.add(builder.build());
             }
         }
 
         return verificationErrors;
     }
+
+    static Set<String> required(final Set<String> options) {
+        return options.stream().filter(o -> !o.startsWith("!")).collect(Collectors.toSet());
+    }
+
+    static Set<String> excluded(final Set<String> options) {
+        return options.stream().filter(o -> o.startsWith("!")).map(o -> o.substring(1)).collect(Collectors.toSet());
+    }
+
+    static Set<String> parameters(final Set<String> options) {
+        final Set<String> withoutExclusionMark = options.stream().map(o -> o.replaceFirst("!", "")).collect(Collectors.toSet());
+
+        return new TreeSet<String>(withoutExclusionMark);
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/d1177cd9/camel-core/src/test/java/org/apache/camel/impl/verifier/ResultErrorHelperTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/impl/verifier/ResultErrorHelperTest.java b/camel-core/src/test/java/org/apache/camel/impl/verifier/ResultErrorHelperTest.java
new file mode 100644
index 0000000..15b82fc
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/impl/verifier/ResultErrorHelperTest.java
@@ -0,0 +1,76 @@
+/**
+ * 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.impl.verifier;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.ComponentVerifier.VerificationError;
+import org.apache.camel.ComponentVerifier.VerificationError.StandardCode;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ResultErrorHelperTest {
+
+    OptionsGroup[] groups = new OptionsGroup[] {OptionsGroup.withName("optionA").options("param1", "param2", "!param3"),
+        OptionsGroup.withName("optionB").options("param1", "!param2", "param3"),
+        OptionsGroup.withName("optionC").options("!param1", "!param2", "param4")};
+
+    @Test
+    public void shouldValidateCorrectParameters() {
+        // just giving param1 and param2 is OK
+        assertTrue(ResultErrorHelper.requiresAny(map("param1", "param2"), groups).isEmpty());
+
+        // just giving param1 and param3 is OK
+        assertTrue(ResultErrorHelper.requiresAny(map("param1", "param3"), groups).isEmpty());
+
+        // just giving param4 is OK
+        assertTrue(ResultErrorHelper.requiresAny(map("param4"), groups).isEmpty());
+    }
+
+    @Test
+    public void shouldValidateParameterExclusions() {
+        // combining param2 and param3 is not OK
+        final List<VerificationError> results = ResultErrorHelper.requiresAny(map("param1", "param2", "param3"),
+            groups);
+
+        assertEquals(3, results.size());
+
+        final VerificationError param3Error = results.get(0);
+        assertEquals(StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION, param3Error.getCode());
+        assertEquals("optionA", param3Error.getDetail(VerificationError.GroupAttribute.GROUP_NAME));
+        assertEquals("param1,param2,param3", param3Error.getDetail(VerificationError.GroupAttribute.GROUP_OPTIONS));
+
+        final VerificationError param2Error = results.get(1);
+        assertEquals(StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION, param2Error.getCode());
+        assertEquals("optionB", param2Error.getDetail(VerificationError.GroupAttribute.GROUP_NAME));
+        assertEquals("param1,param2,param3", param2Error.getDetail(VerificationError.GroupAttribute.GROUP_OPTIONS));
+
+        final VerificationError param4Error = results.get(2);
+        assertEquals(StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION, param4Error.getCode());
+        assertEquals("optionC", param4Error.getDetail(VerificationError.GroupAttribute.GROUP_NAME));
+        assertEquals("param1,param2,param4", param4Error.getDetail(VerificationError.GroupAttribute.GROUP_OPTIONS));
+    }
+
+    static Map<String, Object> map(final String... params) {
+        return Arrays.stream(params).collect(Collectors.toMap(e -> e, e -> "value"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d1177cd9/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
index 0229ccb..438f144 100644
--- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
@@ -61,13 +61,13 @@ public class SalesforceComponentVerifier extends DefaultComponentVerifier {
         // - OAuth JWT Flow
         //
         ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS)
-            .errors(ResultErrorHelper.requiresAny(
-                parameters,
-                OptionsGroup.withName(AuthenticationType.USERNAME_PASSWORD).options("clientId", "clientSecret", "userName", "password"),
-                OptionsGroup.withName(AuthenticationType.REFRESH_TOKEN).options("clientId", "clientSecret", "refreshToken"),
-                OptionsGroup.withName(AuthenticationType.JWT).options("clientId", "userName", "keystore")
-            )
-        );
+            .errors(ResultErrorHelper.requiresAny(parameters,
+                OptionsGroup.withName(AuthenticationType.USERNAME_PASSWORD)
+                    .options("clientId", "clientSecret", "userName", "password", "!refreshToken", "!keystore"),
+                OptionsGroup.withName(AuthenticationType.REFRESH_TOKEN)
+                    .options("clientId", "clientSecret", "refreshToken", "!password", "!keystore"),
+                OptionsGroup.withName(AuthenticationType.JWT)
+                    .options("clientId", "userName", "keystore", "!password", "!refreshToken")));
 
         // Validate using the catalog
         super.verifyParametersAgainstCatalog(builder, parameters);