Expose parameter names as String constants 16/98116/1
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 25 Oct 2021 00:59:29 +0000 (02:59 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 25 Oct 2021 01:01:23 +0000 (03:01 +0200)
Remove static methods in favor of proper constants. This allows us to
use switch statements instead of if/else. Since we are at it,
differentiate between unknown and known-but-invalid parameters.

JIRA: NETCONF-773
Change-Id: Id2a9b09f8cbb5688d1722fc7ee932be4c46ceac6
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
12 files changed:
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/ContentParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/DepthParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/FieldsParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/FilterParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/InsertParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/PointParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestconfQueryParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/StartTimeParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/StopTimeParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/WithDefaultsParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParamsTest.java

index f4f6fa54380b2e22c769afed6d90865b970536ac..6ed8e20f1fc19244ef12a501501beb867f8b89a8 100644 (file)
@@ -30,6 +30,10 @@ public enum ContentParam implements RestconfQueryParam<ContentParam> {
      */
     NONCONFIG("nonconfig");
 
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "content";
+
     private final @NonNull String uriValue;
 
     ContentParam(final String uriValue) {
@@ -43,7 +47,7 @@ public enum ContentParam implements RestconfQueryParam<ContentParam> {
 
     @Override
     public final String paramName() {
-        return uriName();
+        return uriName;
     }
 
     @Override
@@ -51,10 +55,6 @@ public enum ContentParam implements RestconfQueryParam<ContentParam> {
         return uriValue;
     }
 
-    public static @NonNull String uriName() {
-        return "content";
-    }
-
     // Note: returns null of unknowns
     public static @Nullable ContentParam forUriValue(final String uriValue) {
         switch (uriValue) {
index d489c7f0bdcac251f5148a53ad8c9a5f1db13518..8ece8d1e3d6ac383b64b33ee0cfa1123bf902d42 100644 (file)
@@ -19,6 +19,10 @@ import org.eclipse.jdt.annotation.Nullable;
  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.3">RFC8040 section 4.8.2</a>.
  */
 public final class DepthParam implements RestconfQueryParam<DepthParam> {
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "depth";
+
     private static final @NonNull URI CAPABILITY = URI.create("urn:ietf:params:restconf:capability:depth:1.0");
     private static final @NonNull DepthParam MIN = of(1);
     private static final @NonNull DepthParam MAX = of(65535);
@@ -41,7 +45,7 @@ public final class DepthParam implements RestconfQueryParam<DepthParam> {
 
     @Override
     public String paramName() {
-        return uriName();
+        return uriName;
     }
 
     @Override
@@ -49,10 +53,6 @@ public final class DepthParam implements RestconfQueryParam<DepthParam> {
         return String.valueOf(value);
     }
 
-    public static @NonNull String uriName() {
-        return "depth";
-    }
-
     @Beta
     public static @NonNull DepthParam min() {
         return MIN;
index e51b45a090ae0921384e1cf68d235d4f81929c34..247fb43084c2ca78125bb75ab24b4e86392492d0 100644 (file)
@@ -74,6 +74,9 @@ public final class FieldsParam implements RestconfQueryParam<FieldsParam> {
         }
     }
 
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final String uriName = "fields";
     private static final URI CAPABILITY = URI.create("urn:ietf:params:restconf:capability:fields:1.0");
 
     private final ImmutableList<NodeSelector> nodeSelectors;
@@ -82,7 +85,7 @@ public final class FieldsParam implements RestconfQueryParam<FieldsParam> {
     private FieldsParam(final ImmutableList<NodeSelector> nodeSelectors, final String uriValue) {
         this.nodeSelectors = requireNonNull(nodeSelectors);
         checkArgument(!nodeSelectors.isEmpty(), "At least one selector is required");
-        this.paramValue = requireNonNull(uriValue);
+        paramValue = requireNonNull(uriValue);
     }
 
     /**
@@ -103,11 +106,7 @@ public final class FieldsParam implements RestconfQueryParam<FieldsParam> {
 
     @Override
     public String paramName() {
-        return uriName();
-    }
-
-    public static String uriName() {
-        return "fields";
+        return uriName;
     }
 
     public static URI capabilityUri() {
index f66bd2dfd231c9c4bae853d29be5037f32123433..67f56eed357eb385eb9948f83bc2718376386129 100644 (file)
@@ -18,6 +18,10 @@ import org.eclipse.jdt.annotation.NonNull;
  */
 
 public final class FilterParam implements RestconfQueryParam<FilterParam> {
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "filter";
+
     private static final @NonNull URI CAPABILITY = URI.create("urn:ietf:params:restconf:capability:filter:1.0");
 
     // FIXME: can we have a parsed, but not bound version of an XPath, please?
@@ -34,7 +38,7 @@ public final class FilterParam implements RestconfQueryParam<FilterParam> {
 
     @Override
     public String paramName() {
-        return uriName();
+        return uriName;
     }
 
     @Override
@@ -46,10 +50,6 @@ public final class FilterParam implements RestconfQueryParam<FilterParam> {
         return new FilterParam(uriValue);
     }
 
-    public static @NonNull String uriName() {
-        return "filter";
-    }
-
     public static @NonNull URI capabilityUri() {
         return CAPABILITY;
     }
index 206e66a627f09a2fdd5012ecdc74dbb0abf45ff2..4c60198c8687769984b490cc0e46d954d353be8b 100644 (file)
@@ -34,6 +34,10 @@ public enum InsertParam implements RestconfQueryParam<InsertParam> {
      */
     LAST("last");
 
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "insert";
+
     private @NonNull String uriValue;
 
     InsertParam(final String uriValue) {
@@ -47,7 +51,7 @@ public enum InsertParam implements RestconfQueryParam<InsertParam> {
 
     @Override
     public String paramName() {
-        return uriName();
+        return uriName;
     }
 
     @Override
@@ -55,10 +59,6 @@ public enum InsertParam implements RestconfQueryParam<InsertParam> {
         return uriValue;
     }
 
-    public static @NonNull String uriName() {
-        return "insert";
-    }
-
     // Note: returns null of unknowns
     public static @Nullable InsertParam forUriValue(final String uriValue) {
         switch (uriValue) {
index 1cfc3c01c8293d9404e3f8e92c0609d422836510..9e9c3b9b4b82aa5fb95eb54fedbd1e5247fb5d89 100644 (file)
@@ -18,6 +18,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
  */
 @NonNullByDefault
 public final class PointParam implements RestconfQueryParam<PointParam> {
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final String uriName = "point";
+
     // FIXME: This should be ApiPath
     private final String value;
 
@@ -32,7 +36,7 @@ public final class PointParam implements RestconfQueryParam<PointParam> {
 
     @Override
     public String paramName() {
-        return uriName();
+        return uriName;
     }
 
     @Override
@@ -44,10 +48,6 @@ public final class PointParam implements RestconfQueryParam<PointParam> {
         return new PointParam(uriValue);
     }
 
-    public static String uriName() {
-        return "point";
-    }
-
     public String value() {
         return value;
     }
index f3f43996dd282a2af8e964ded34f83c367bcfc83..f14a72095de992338c6df8b77bf5e18af4e51b2a 100644 (file)
@@ -13,6 +13,14 @@ import org.opendaylight.yangtools.concepts.Immutable;
 /**
  * Interface implemented by all Java classes which represent a
  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-4.8">RESTCONF query parameter</a>.
+ *
+ * <p>
+ * Implementations of this interface are required to expose a {@code public static @NonNull uriName} constant, which
+ * holds the well-known URI Request Query Parameter name of the associated definition.
+ *
+ * <p>
+ * This naming violates the usual Java coding style, we need it to keep API consistency as an enum can be used as an
+ * implementation, in which case users could be confused by upper-case constants which are not enum members.
  */
 // FIXME: sealed when we have JDK17+?
 public interface RestconfQueryParam<T extends RestconfQueryParam<T>> extends Immutable {
index 3d4c0acae892eb6b24f8a3b43e319e85e32a64ed..4fbecb87a13c858e8131118d345e3811c486c762 100644 (file)
@@ -15,6 +15,10 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.7">RFC8040 section 4.8.7</a>.
  */
 public final class StartTimeParam extends AbstractReplayParam<StartTimeParam> {
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "start-time";
+
     private StartTimeParam(final DateAndTime value) {
         super(value);
     }
@@ -30,11 +34,7 @@ public final class StartTimeParam extends AbstractReplayParam<StartTimeParam> {
 
     @Override
     public String paramName() {
-        return uriName();
-    }
-
-    public static @NonNull String uriName() {
-        return "start-time";
+        return uriName;
     }
 
     public static @NonNull StartTimeParam forUriValue(final String uriValue) {
index 0c3fa6cbc4417a967b7b3e41ecc579a878023fcb..51067d4cdec087b4b12453fd3e590cbf0dec547b 100644 (file)
@@ -15,6 +15,10 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.8">RFC8040 section 4.8.8</a>.
  */
 public final class StopTimeParam extends AbstractReplayParam<StopTimeParam> {
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "stop-time";
+
     private StopTimeParam(final DateAndTime value) {
         super(value);
     }
@@ -30,11 +34,7 @@ public final class StopTimeParam extends AbstractReplayParam<StopTimeParam> {
 
     @Override
     public String paramName() {
-        return uriName();
-    }
-
-    public static @NonNull String uriName() {
-        return "stop-time";
+        return uriName;
     }
 
     public static @NonNull StopTimeParam forUriValue(final String uriValue) {
index daf4e77ca5daa2226ca4f4e3901107f91884ef34..aa9f7ac045746f1a43fe6ee777a5298b28e633b8 100644 (file)
@@ -35,6 +35,10 @@ public enum WithDefaultsParam implements RestconfQueryParam<WithDefaultsParam> {
      */
     TRIM("trim");
 
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final @NonNull String uriName = "with-defaults";
+
     private static final @NonNull URI CAPABILITY = URI.create("urn:ietf:params:restconf:capability:with-defaults:1.0");
 
     private final @NonNull String uriValue;
@@ -50,7 +54,7 @@ public enum WithDefaultsParam implements RestconfQueryParam<WithDefaultsParam> {
 
     @Override
     public String paramName() {
-        return uriName();
+        return uriName;
     }
 
     @Override
@@ -58,10 +62,6 @@ public enum WithDefaultsParam implements RestconfQueryParam<WithDefaultsParam> {
         return uriValue;
     }
 
-    public static @NonNull String uriName() {
-        return "with-defaults";
-    }
-
     public static @Nullable WithDefaultsParam forUriValue(final String uriValue) {
         switch (uriValue) {
             case "explicit":
index f3c766bb20d67838d4be7298134f16b969133d1d..db7bb59c3fdb82ac2ef0e4312fead2e317af7ae1 100644 (file)
@@ -44,14 +44,20 @@ import org.opendaylight.yangtools.yang.common.ErrorType;
 
 @Beta
 public final class QueryParams {
-    private static final Set<String> ALLOWED_PARAMETERS = Set.of(ContentParam.uriName(), DepthParam.uriName(),
-        FieldsParam.uriName(), WithDefaultsParam.uriName());
     private static final List<String> POSSIBLE_CONTENT = Arrays.stream(ContentParam.values())
         .map(ContentParam::paramValue)
         .collect(Collectors.toUnmodifiableList());
     private static final List<String> POSSIBLE_WITH_DEFAULTS = Arrays.stream(WithDefaultsParam.values())
         .map(WithDefaultsParam::paramValue)
         .collect(Collectors.toUnmodifiableList());
+    private static final Set<String> KNOWN_PARAMS = Set.of(
+        // Read data
+        ContentParam.uriName, DepthParam.uriName, FieldsParam.uriName, WithDefaultsParam.uriName,
+        // Modify data
+        InsertParam.uriName, PointParam.uriName,
+        // Notifications
+        FilterParam.uriName, StartTimeParam.uriName, StopTimeParam.uriName ,"odl-skip-notification-data");
+
 
     private QueryParams() {
         // Utility class
@@ -68,23 +74,26 @@ public final class QueryParams {
             final List<String> paramValues = entry.getValue();
 
             try {
-                if (paramName.equals(StartTimeParam.uriName())) {
-                    startTime = optionalParam(StartTimeParam::forUriValue, paramName, paramValues);
-                    break;
-                } else if (paramName.equals(StopTimeParam.uriName())) {
-                    stopTime = optionalParam(StopTimeParam::forUriValue, paramName, paramValues);
-                    break;
-                } else if (paramName.equals(FilterParam.uriName())) {
-                    filter = optionalParam(FilterParam::forUriValue, paramName, paramValues);
-                } else if (paramName.equals("odl-skip-notification-data")) {
-                    // FIXME: this should be properly encapsulated in SkipNotificatioDataParameter
-                    skipNotificationData = Boolean.parseBoolean(optionalParam(paramName, paramValues));
-                } else {
-                    throw new RestconfDocumentedException("Bad parameter used with notifications: " + paramName,
-                        ErrorType.PROTOCOL, ErrorTag. UNKNOWN_ATTRIBUTE);
+                switch (paramName) {
+                    case FilterParam.uriName:
+                        filter = optionalParam(FilterParam::forUriValue, paramName, paramValues);
+                        break;
+                    case StartTimeParam.uriName:
+                        startTime = optionalParam(StartTimeParam::forUriValue, paramName, paramValues);
+                        break;
+                    case StopTimeParam.uriName:
+                        stopTime = optionalParam(StopTimeParam::forUriValue, paramName, paramValues);
+                        break;
+                    case "odl-skip-notification-data":
+                        // FIXME: this should be properly encapsulated in SkipNotificatioDataParameter
+                        skipNotificationData = Boolean.parseBoolean(optionalParam(paramName, paramValues));
+                        break;
+                    default:
+                        throw unhandledParam("notification", paramName);
                 }
             } catch (IllegalArgumentException e) {
-                throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(), e);
+                throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(),
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e);
             }
         }
 
@@ -124,60 +133,64 @@ public final class QueryParams {
             final String paramName = entry.getKey();
             final List<String> paramValues = entry.getValue();
 
-            if (paramName.equals(ContentParam.uriName())) {
-                final String str = optionalParam(paramName, paramValues);
-                if (str != null) {
-                    content = RestconfDocumentedException.throwIfNull(ContentParam.forUriValue(str),
-                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
-                        "Invalid content parameter: %s, allowed values are %s", str, POSSIBLE_CONTENT);
-                }
-            } else if (paramName.equals(DepthParam.uriName())) {
-                final String str = optionalParam(paramName, paramValues);
-                try {
-                    depth = DepthParam.forUriValue(str);
-                } catch (IllegalArgumentException e) {
-                    throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
-                        ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + str, null,
-                        "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
-                }
-            } else if (paramName.equals(FieldsParam.uriName())) {
-                final String str = optionalParam(paramName, paramValues);
-                if (str != null) {
+            switch (paramName) {
+                case ContentParam.uriName:
+                    final String contentStr = optionalParam(paramName, paramValues);
+                    if (contentStr != null) {
+                        content = RestconfDocumentedException.throwIfNull(ContentParam.forUriValue(contentStr),
+                            ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                            "Invalid content parameter: %s, allowed values are %s", contentStr, POSSIBLE_CONTENT);
+                    }
+                    break;
+                case DepthParam.uriName:
+                    final String depthStr = optionalParam(paramName, paramValues);
                     try {
-                        fields = FieldsParam.parse(str);
-                    } catch (ParseException e) {
+                        depth = DepthParam.forUriValue(depthStr);
+                    } catch (IllegalArgumentException e) {
                         throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
-                            ErrorTag.INVALID_VALUE, "Invalid filds parameter: " + str));
+                            ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depthStr, null,
+                            "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
                     }
-                }
-            } else if (paramName.equals(WithDefaultsParam.uriName())) {
-                final String str = optionalParam(paramName, paramValues);
-                if (str != null) {
-                    final WithDefaultsParam val = WithDefaultsParam.forUriValue(str);
-                    if (val == null) {
-                        throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL,
-                            ErrorTag.INVALID_VALUE, "Invalid with-defaults parameter: " + str, null,
-                            "The with-defaults parameter must be a string in " + POSSIBLE_WITH_DEFAULTS));
+                    break;
+                case FieldsParam.uriName:
+                    final String fieldsStr = optionalParam(paramName, paramValues);
+                    if (fieldsStr != null) {
+                        try {
+                            fields = FieldsParam.parse(fieldsStr);
+                        } catch (ParseException e) {
+                            throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
+                                ErrorTag.INVALID_VALUE, "Invalid filds parameter: " + fieldsStr));
+                        }
                     }
+                    break;
+                case WithDefaultsParam.uriName:
+                    final String withDefaultsStr = optionalParam(paramName, paramValues);
+                    if (withDefaultsStr != null) {
+                        final WithDefaultsParam val = WithDefaultsParam.forUriValue(withDefaultsStr);
+                        if (val == null) {
+                            throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL,
+                                ErrorTag.INVALID_VALUE, "Invalid with-defaults parameter: " + withDefaultsStr, null,
+                                "The with-defaults parameter must be a string in " + POSSIBLE_WITH_DEFAULTS));
+                        }
 
-                    switch (val) {
-                        case REPORT_ALL:
-                            withDefaults = null;
-                            tagged = false;
-                            break;
-                        case REPORT_ALL_TAGGED:
-                            withDefaults = null;
-                            tagged = true;
-                            break;
-                        default:
-                            withDefaults = val;
-                            tagged = false;
+                        switch (val) {
+                            case REPORT_ALL:
+                                withDefaults = null;
+                                tagged = false;
+                                break;
+                            case REPORT_ALL_TAGGED:
+                                withDefaults = null;
+                                tagged = true;
+                                break;
+                            default:
+                                withDefaults = val;
+                                tagged = false;
+                        }
                     }
-                }
-            } else {
-                // FIXME: recognize pretty-print here
-                throw new RestconfDocumentedException("Not allowed parameter for read operation: " + paramName,
-                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE);
+                    break;
+                default:
+                    // FIXME: recognize pretty-print here
+                    throw unhandledParam("read", paramName);
             }
         }
 
@@ -191,23 +204,26 @@ public final class QueryParams {
         for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
             final String uriName = entry.getKey();
             final List<String> paramValues = entry.getValue();
-            if (uriName.equals(InsertParam.uriName())) {
-                final String str = optionalParam(uriName, paramValues);
-                if (str != null) {
-                    insert = InsertParam.forUriValue(str);
-                    if (insert == null) {
-                        throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'",
-                            ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
+            switch (uriName) {
+                case InsertParam.uriName:
+                    final String instartStr = optionalParam(uriName, paramValues);
+                    if (instartStr != null) {
+                        insert = InsertParam.forUriValue(instartStr);
+                        if (insert == null) {
+                            throw new RestconfDocumentedException(
+                                "Unrecognized insert parameter value '" + instartStr + "'", ErrorType.PROTOCOL,
+                                ErrorTag.BAD_ELEMENT);
+                        }
                     }
-                }
-            } else if (PointParam.uriName().equals(uriName)) {
-                final String str = optionalParam(uriName, paramValues);
-                if (str != null) {
-                    point = PointParam.forUriValue(str);
-                }
-            } else {
-                throw new RestconfDocumentedException("Bad parameter for post: " + uriName,
-                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE);
+                    break;
+                case PointParam.uriName:
+                    final String pointStr = optionalParam(uriName, paramValues);
+                    if (pointStr != null) {
+                        point = PointParam.forUriValue(pointStr);
+                    }
+                    break;
+                default:
+                    throw unhandledParam("write", uriName);
             }
         }
 
@@ -218,6 +234,14 @@ public final class QueryParams {
         }
     }
 
+    private static RestconfDocumentedException unhandledParam(final String operation, final String name) {
+        return KNOWN_PARAMS.contains(name)
+            ? new RestconfDocumentedException("Invalid parameter in " + operation + ": " + name,
+                ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE)
+            : new RestconfDocumentedException("Unknown parameter in " + operation + ": " + name,
+                ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE);
+    }
+
     @VisibleForTesting
     static @Nullable String optionalParam(final String name, final List<String> values) {
         switch (values.size()) {
index 4f2b51d38cbd469e9421e2c71e2ab76c1d56a153..995828d72f60f58e06f74fb3c17a45bf128f499b 100644 (file)
@@ -11,26 +11,27 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 import java.util.List;
 import java.util.Set;
+import java.util.function.Function;
 import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriInfo;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.nb.rfc8040.ContentParam;
 import org.opendaylight.restconf.nb.rfc8040.DepthParam;
-import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
+import org.opendaylight.restconf.nb.rfc8040.InsertParam;
+import org.opendaylight.restconf.nb.rfc8040.RestconfQueryParam;
 import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParam;
 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
@@ -42,23 +43,12 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class QueryParamsTest {
-    @Mock
-    public InstanceIdentifierContext<ContainerSchemaNode> context;
-    @Mock
-    public UriInfo uriInfo;
-    @Mock
-    public EffectiveModelContext modelContext;
-    @Mock
-    public ContainerSchemaNode containerSchema;
-    @Mock
-    public LeafSchemaNode containerChildSchema;
-
     /**
      * Test when parameter is present at most once.
      */
     @Test
     public void optionalParamTest() {
-        assertEquals("all", QueryParams.optionalParam(ContentParam.uriName(), List.of("all")));
+        assertEquals("all", QueryParams.optionalParam(ContentParam.uriName, List.of("all")));
     }
 
     /**
@@ -67,7 +57,7 @@ public class QueryParamsTest {
     @Test
     public void optionalParamMultipleTest() {
         final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.optionalParam(ContentParam.uriName(), List.of("config", "nonconfig", "all")));
+            () -> QueryParams.optionalParam(ContentParam.uriName, List.of("config", "nonconfig", "all")));
         final List<RestconfError> errors = ex.getErrors();
         assertEquals(1, errors.size());
 
@@ -81,16 +71,13 @@ public class QueryParamsTest {
      */
     @Test
     public void checkParametersTypesNegativeTest() {
-        mockQueryParameter("not-allowed-parameter", "does-not-matter");
-
-        final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.newWriteDataParams(uriInfo));
-        final List<RestconfError> errors = ex.getErrors();
-        assertEquals(1, errors.size());
+        assertUnknownParam(QueryParams::newNotificationQueryParams);
+        assertUnknownParam(QueryParams::newReadDataParams);
+        assertUnknownParam(QueryParams::newWriteDataParams);
 
-        final RestconfError error = errors.get(0);
-        assertEquals("Error type is not correct", ErrorType.PROTOCOL, error.getErrorType());
-        assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, error.getErrorTag());
+        assertInvalidParam(QueryParams::newNotificationQueryParams, ContentParam.ALL);
+        assertInvalidParam(QueryParams::newReadDataParams, InsertParam.LAST);
+        assertInvalidParam(QueryParams::newWriteDataParams, ContentParam.ALL);
     }
 
     /**
@@ -99,86 +86,22 @@ public class QueryParamsTest {
     @Test
     public void parseUriParametersDefaultTest() {
         // no parameters, default values should be used
-        mockQueryParameters(new MultivaluedHashMap<String, String>());
-
-        final var parsedParameters = QueryParams.newReadDataParams(uriInfo);
-        assertEquals(ContentParam.ALL, parsedParameters.content());
-        assertNull(parsedParameters.depth());
-        assertNull(parsedParameters.fields());
+        final var params = assertParams(QueryParams::newReadDataParams, new MultivaluedHashMap<String, String>());
+        assertEquals(ContentParam.ALL, params.content());
+        assertNull(params.depth());
+        assertNull(params.fields());
     }
 
-    /**
-     * Testing parsing of with-defaults parameter which value which is not supported.
-     */
     @Test
-    public void parseUriParametersWithDefaultInvalidTest() {
-        // preparation of input data
-        mockQueryParameter("with-defaults", "invalid");
-
-        final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.newReadDataParams(uriInfo));
-        final List<RestconfError> errors = ex.getErrors();
-        assertEquals(1, errors.size());
-        assertEquals(ErrorTag.INVALID_VALUE, errors.get(0).getErrorTag());
-    }
-
-    /**
-     * Negative test of parsing request URI parameters when depth parameter has not allowed value.
-     */
-    @Test
-    public void parseUriParametersDepthParameterNegativeTest() {
-        // inserted value is not allowed
-        mockQueryParameter("depth", "bounded");
-
-        RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.newReadDataParams(uriInfo));
-        // Bad request
-        assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
-        assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
-    }
-
-    /**
-     * Negative test of parsing request URI parameters when content parameter has not allowed value.
-     */
-    @Test
-    public void parseUriParametersContentParameterNegativeTest() {
-        mockQueryParameter("content", "not-allowed-parameter-value");
-
-        final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.newReadDataParams(uriInfo));
-        // Bad request
-        assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
-        assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
-    }
+    public void testInvalidValueReadDataParams() {
+        assertInvalidValue(QueryParams::newReadDataParams, ContentParam.uriName);
+        assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName);
+        assertInvalidValue(QueryParams::newReadDataParams, WithDefaultsParam.uriName);
 
-    /**
-     * Negative test of parsing request URI parameters when depth parameter has not allowed value (more than maximum).
-     */
-    @Test
-    public void parseUriParametersDepthMaximalParameterNegativeTest() {
         // inserted value is too high
-        mockQueryParameter("depth", "65536");
-
-        RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.newReadDataParams(uriInfo));
-        // Bad request
-        assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
-        assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
-    }
-
-    /**
-     * Negative test of parsing request URI parameters when depth parameter has not allowed value (less than minimum).
-     */
-    @Test
-    public void parseUriParametersDepthMinimalParameterNegativeTest() {
+        assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "65536");
         // inserted value is too low
-        mockQueryParameter("depth", "0");
-
-        RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
-            () -> QueryParams.newReadDataParams(uriInfo));
-        // Bad request
-        assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
-        assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag());
+        assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "0");
     }
 
     /**
@@ -187,12 +110,9 @@ public class QueryParamsTest {
      */
     @Test
     public void parseUriParametersWithDefaultAndTaggedTest() {
-        // preparation of input data
-        mockQueryParameter("with-defaults", "report-all-tagged");
-
-        final var parsedParameters = QueryParams.newReadDataParams(uriInfo);
-        assertNull(parsedParameters.withDefaults());
-        assertTrue(parsedParameters.tagged());
+        final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all-tagged");
+        assertNull(params.withDefaults());
+        assertTrue(params.tagged());
     }
 
     /**
@@ -201,12 +121,9 @@ public class QueryParamsTest {
      */
     @Test
     public void parseUriParametersWithDefaultAndReportAllTest() {
-        // preparation of input data
-        mockQueryParameter("with-defaults", "report-all");
-
-        final var parsedParameters = QueryParams.newReadDataParams(uriInfo);
-        assertNull(parsedParameters.withDefaults());
-        assertFalse(parsedParameters.tagged());
+        final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all");
+        assertNull(params.withDefaults());
+        assertFalse(params.tagged());
     }
 
     /**
@@ -215,12 +132,9 @@ public class QueryParamsTest {
      */
     @Test
     public void parseUriParametersWithDefaultAndNonTaggedTest() {
-        // preparation of input data
-        mockQueryParameter("with-defaults", "explicit");
-
-        final var parsedParameters = QueryParams.newReadDataParams(uriInfo);
-        assertSame(WithDefaultsParam.EXPLICIT, parsedParameters.withDefaults());
-        assertFalse(parsedParameters.tagged());
+        final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "explicit");
+        assertEquals(WithDefaultsParam.EXPLICIT, params.withDefaults());
+        assertFalse(params.tagged());
     }
 
     /**
@@ -230,45 +144,89 @@ public class QueryParamsTest {
     public void parseUriParametersUserDefinedTest() {
         final QName containerChild = QName.create("ns", "container-child");
 
-        final MultivaluedMap<String, String> parameters = new MultivaluedHashMap<>();
+        final var parameters = new MultivaluedHashMap<String, String>();
         parameters.putSingle("content", "config");
         parameters.putSingle("depth", "10");
         parameters.putSingle("fields", "container-child");
-        mockQueryParameters(parameters);
 
-        final ReadDataParams parsedParameters = QueryParams.newReadDataParams(uriInfo);
+        final var params = assertParams(QueryParams::newReadDataParams, parameters);
         // content
-        assertEquals(ContentParam.CONFIG, parsedParameters.content());
+        assertEquals(ContentParam.CONFIG, params.content());
 
         // depth
-        final DepthParam depth = parsedParameters.depth();
+        final DepthParam depth = params.depth();
         assertNotNull(depth);
         assertEquals(10, depth.value());
 
         // fields
-        assertNotNull(parsedParameters.fields());
+        assertNotNull(params.fields());
 
         // fields for write filtering
+        final var containerSchema = mock(ContainerSchemaNode.class);
         doReturn(QName.create(containerChild, "container")).when(containerSchema).getQName();
-        doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild);
+        final var containerChildSchema = mock(LeafSchemaNode.class);
         doReturn(containerChild).when(containerChildSchema).getQName();
+        doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild);
+        final var context = mock(InstanceIdentifierContext.class);
+        final var modelContext = mock(EffectiveModelContext.class);
         doReturn(modelContext).when(context).getSchemaContext();
         doReturn(containerSchema).when(context).getSchemaNode();
 
-        final QueryParameters queryParameters = QueryParams.newQueryParameters(parsedParameters, context);
+        final QueryParameters queryParameters = QueryParams.newQueryParameters(params, context);
         final List<Set<QName>> fields = queryParameters.fields();
         assertNotNull(fields);
         assertEquals(1, fields.size());
         assertEquals(Set.of(containerChild), fields.get(0));
     }
 
-    private void mockQueryParameter(final String name, final String value) {
-        final MultivaluedMap<String, String> parameters = new MultivaluedHashMap<>();
-        parameters.putSingle(name, value);
-        mockQueryParameters(parameters);
+    private static void assertInvalidParam(final Function<UriInfo, ?> paramsMethod, final RestconfQueryParam<?> param) {
+        final var params = new MultivaluedHashMap<String, String>();
+        params.putSingle(param.paramName(), "odl-test-value");
+        assertParamsThrows(ErrorTag.MALFORMED_MESSAGE, paramsMethod, params);
+    }
+
+    private static void assertUnknownParam(final Function<UriInfo, ?> paramsMethod) {
+        final var params = new MultivaluedHashMap<String, String>();
+        params.putSingle("odl-unknown-param", "odl-test-value");
+        assertParamsThrows(ErrorTag.UNKNOWN_ATTRIBUTE, paramsMethod, params);
+    }
+
+    private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name) {
+        assertInvalidValue(paramsMethod, name, "odl-invalid-value");
+    }
+
+    private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name,
+            final String value) {
+        final var params = new MultivaluedHashMap<String, String>();
+        params.putSingle(name, value);
+        assertParamsThrows(ErrorTag.INVALID_VALUE, paramsMethod, params);
+    }
+
+    private static void assertParamsThrows(final ErrorTag expectedTag, final Function<UriInfo, ?> paramsMethod,
+            final MultivaluedMap<String, String> params) {
+        final var uriInfo = mock(UriInfo.class);
+        doReturn(params).when(uriInfo).getQueryParameters();
+
+        final var ex = assertThrows(RestconfDocumentedException.class,  () -> paramsMethod.apply(uriInfo));
+        final var errors = ex.getErrors();
+        assertEquals(1, errors.size());
+
+        final var error = errors.get(0);
+        assertEquals(ErrorType.PROTOCOL, error.getErrorType());
+        assertEquals(expectedTag, error.getErrorTag());
+    }
+
+    private static <T> T assertParams(final Function<UriInfo, T> paramsMethod, final String name,
+            final String value) {
+        final var params = new MultivaluedHashMap<String, String>();
+        params.putSingle(name, value);
+        return assertParams(paramsMethod, params);
     }
 
-    private void mockQueryParameters(final MultivaluedMap<String, String> parameters) {
-        doReturn(parameters).when(uriInfo).getQueryParameters();
+    private static <T> T assertParams(final Function<UriInfo, T> paramsMethod,
+            final MultivaluedMap<String, String> params) {
+        final var uriInfo = mock(UriInfo.class);
+        doReturn(params).when(uriInfo).getQueryParameters();
+        return paramsMethod.apply(uriInfo);
     }
 }