Unify parameter parsing 38/98138/1
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 26 Oct 2021 12:16:37 +0000 (14:16 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 26 Oct 2021 12:30:23 +0000 (14:30 +0200)
We have a number of different ways to parse a query parameter, make the
contracts more consistent.

JIRA: NETCONF-773
Change-Id: I7c3090998efaeb9b7d713e595640a6d1e156fc65
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
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/FieldsParam.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/WithDefaultsParam.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java

index 6ed8e20f1fc19244ef12a501501beb867f8b89a8..75f2255ad4d3f5c65a64dec80efaedac0ee64216 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.restconf.nb.rfc8040;
 import static java.util.Objects.requireNonNull;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
 
 /**
  * Enumeration of possible {@code content} values as defined by
@@ -55,8 +54,7 @@ public enum ContentParam implements RestconfQueryParam<ContentParam> {
         return uriValue;
     }
 
-    // Note: returns null of unknowns
-    public static @Nullable ContentParam forUriValue(final String uriValue) {
+    public static @NonNull ContentParam forUriValue(final String uriValue) {
         switch (uriValue) {
             case "all":
                 return ALL;
@@ -65,7 +63,8 @@ public enum ContentParam implements RestconfQueryParam<ContentParam> {
             case "nonconfig":
                 return NONCONFIG;
             default:
-                return null;
+                throw new IllegalArgumentException(
+                    "Value can be 'all', 'config' or 'non-config', not '" + uriValue + "'");
         }
     }
 }
index d6b96e1cac50492fd98968191d3a7f68d8c0bb4f..3142a9372cb33d7efeeaf9c60eccccd7f875c511 100644 (file)
@@ -113,6 +113,14 @@ public final class FieldsParam implements RestconfQueryParam<FieldsParam> {
         return new FieldsParameterParser().parse(str);
     }
 
+    public static FieldsParam forUriValue(final String uriValue) {
+        try {
+            return parse(uriValue);
+        } catch (ParseException e) {
+            throw new IllegalArgumentException(e.getMessage() + " [at offset " + e.getErrorOffset() + "]", e);
+        }
+    }
+
     @Override
     public Class<@NonNull FieldsParam> javaClass() {
         return FieldsParam.class;
index 4c60198c8687769984b490cc0e46d954d353be8b..829a06a8b78f5c893468d7fa08b515e26e81edb7 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.restconf.nb.rfc8040;
 import static java.util.Objects.requireNonNull;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
 
 /**
  * Enumeration of possible {@code insert} values as defined by
@@ -59,8 +58,7 @@ public enum InsertParam implements RestconfQueryParam<InsertParam> {
         return uriValue;
     }
 
-    // Note: returns null of unknowns
-    public static @Nullable InsertParam forUriValue(final String uriValue) {
+    public static @NonNull InsertParam forUriValue(final String uriValue) {
         switch (uriValue) {
             case "after":
                 return AFTER;
@@ -71,7 +69,8 @@ public enum InsertParam implements RestconfQueryParam<InsertParam> {
             case "last":
                 return LAST;
             default:
-                return null;
+                throw new IllegalArgumentException(
+                    "Value can be 'after', 'before', 'first' or 'last', not '" + uriValue + "'");
         }
     }
 }
\ No newline at end of file
index 9e9c3b9b4b82aa5fb95eb54fedbd1e5247fb5d89..a80135f29153d06dfac4aba42f166acd4ab399a6 100644 (file)
@@ -10,20 +10,18 @@ package org.opendaylight.restconf.nb.rfc8040;
 import static java.util.Objects.requireNonNull;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.NonNullByDefault;
 
 /**
  * This class represents a {@code point} parameter as defined in
  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.4">RFC8040 section 4.8.4</a>.
  */
-@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";
+    public static final @NonNull String uriName = "point";
 
     // FIXME: This should be ApiPath
-    private final String value;
+    private final @NonNull String value;
 
     private PointParam(final String value) {
         this.value = requireNonNull(value);
@@ -44,11 +42,11 @@ public final class PointParam implements RestconfQueryParam<PointParam> {
         return value;
     }
 
-    public static PointParam forUriValue(final String uriValue) {
+    public static @NonNull PointParam forUriValue(final String uriValue) {
         return new PointParam(uriValue);
     }
 
-    public String value() {
+    public @NonNull String value() {
         return value;
     }
 }
index aa9f7ac045746f1a43fe6ee777a5298b28e633b8..6caa650c83d4e2eba8bf7969eab35d91de0786ae 100644 (file)
@@ -11,7 +11,6 @@ import static java.util.Objects.requireNonNull;
 
 import java.net.URI;
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
 
 /**
  * Enumeration of possible {@code with-defaults} parameter values as defined by
@@ -62,7 +61,7 @@ public enum WithDefaultsParam implements RestconfQueryParam<WithDefaultsParam> {
         return uriValue;
     }
 
-    public static @Nullable WithDefaultsParam forUriValue(final String uriValue) {
+    public static @NonNull WithDefaultsParam forUriValue(final String uriValue) {
         switch (uriValue) {
             case "explicit":
                 return EXPLICIT;
@@ -73,7 +72,8 @@ public enum WithDefaultsParam implements RestconfQueryParam<WithDefaultsParam> {
             case "trim":
                 return TRIM;
             default:
-                return null;
+                throw new IllegalArgumentException(
+                    "Value can be 'explicit', 'report-all', 'report-all-tagged' or 'trim', not '" + uriValue + "'");
         }
     }
 
index 9d7b7b459b022e0900c1c2c0fa48f5d8828215fd..f2e2998a6db1b505b67142ccf72879386b8b2b45 100644 (file)
@@ -11,13 +11,10 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
-import java.text.ParseException;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.Function;
-import java.util.stream.Collectors;
 import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
@@ -47,20 +44,15 @@ import org.opendaylight.yangtools.yang.common.ErrorType;
 
 @Beta
 public final class QueryParams {
-    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,
+        PrettyPrintParam.uriName,
         // Modify data
         InsertParam.uriName, PointParam.uriName,
         // Notifications
-        FilterParam.uriName, StartTimeParam.uriName, StopTimeParam.uriName ,"odl-skip-notification-data");
-
+        FilterParam.uriName, StartTimeParam.uriName, StopTimeParam.uriName,
+        LeafNodesOnlyParam.uriName, SkipNotificationDataParam.uriName);
 
     private QueryParams() {
         // Utility class
@@ -141,66 +133,51 @@ public final class QueryParams {
             final String paramName = entry.getKey();
             final List<String> paramValues = entry.getValue();
 
-            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 {
-                        depth = DepthParam.forUriValue(depthStr);
-                    } catch (IllegalArgumentException e) {
-                        throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
-                            ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depthStr, null,
-                            "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
-                    }
-                    break;
-                case FieldsParam.uriName:
-                    final String fieldsStr = optionalParam(paramName, paramValues);
-                    if (fieldsStr != null) {
+            try {
+                switch (paramName) {
+                    case ContentParam.uriName:
+                        content = optionalParam(ContentParam::forUriValue, paramName, paramValues);
+                        break;
+                    case DepthParam.uriName:
+                        final String depthStr = optionalParam(paramName, paramValues);
                         try {
-                            fields = FieldsParam.parse(fieldsStr);
-                        } catch (ParseException e) {
+                            depth = DepthParam.forUriValue(depthStr);
+                        } catch (IllegalArgumentException e) {
                             throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
-                                ErrorTag.INVALID_VALUE, "Invalid filds parameter: " + fieldsStr));
+                                ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depthStr, null,
+                                "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
                         }
-                    }
-                    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;
+                        break;
+                    case FieldsParam.uriName:
+                        fields = optionalParam(FieldsParam::forUriValue, paramName, paramValues);
+                        break;
+                    case WithDefaultsParam.uriName:
+                        final var defaultsVal = optionalParam(WithDefaultsParam::forUriValue, paramName, paramValues);
+                        if (defaultsVal != null) {
+                            switch (defaultsVal) {
+                                case REPORT_ALL:
+                                    withDefaults = null;
+                                    tagged = false;
+                                    break;
+                                case REPORT_ALL_TAGGED:
+                                    withDefaults = null;
+                                    tagged = true;
+                                    break;
+                                default:
+                                    withDefaults = defaultsVal;
+                                    tagged = false;
+                            }
                         }
-                    }
-                    break;
-                case PrettyPrintParam.uriName:
-                    prettyPrint = optionalParam(PrettyPrintParam::forUriValue, paramName, paramValues);
-                    break;
-                default:
-                    throw unhandledParam("read", paramName);
+                        break;
+                    case PrettyPrintParam.uriName:
+                        prettyPrint = optionalParam(PrettyPrintParam::forUriValue, paramName, paramValues);
+                        break;
+                    default:
+                        throw unhandledParam("read", paramName);
+                }
+            } catch (IllegalArgumentException e) {
+                throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(),
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e);
             }
         }
 
@@ -212,28 +189,23 @@ public final class QueryParams {
         PointParam point = null;
 
         for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
-            final String uriName = entry.getKey();
+            final String paramName = entry.getKey();
             final List<String> paramValues = entry.getValue();
-            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);
-                        }
-                    }
-                    break;
-                case PointParam.uriName:
-                    final String pointStr = optionalParam(uriName, paramValues);
-                    if (pointStr != null) {
-                        point = PointParam.forUriValue(pointStr);
-                    }
-                    break;
-                default:
-                    throw unhandledParam("write", uriName);
+
+            try {
+                switch (paramName) {
+                    case InsertParam.uriName:
+                        insert = optionalParam(InsertParam::forUriValue, paramName, paramValues);
+                        break;
+                    case PointParam.uriName:
+                        point = optionalParam(PointParam::forUriValue, paramName, paramValues);
+                        break;
+                    default:
+                        throw unhandledParam("write", paramName);
+                }
+            } catch (IllegalArgumentException e) {
+                throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(),
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e);
             }
         }