import org.opendaylight.yangtools.concepts.Immutable;
/**
- * Parser and holder of query paramteres from uriInfo for notifications.
+ * Parser and holder of query parameters from uriInfo for notifications.
*/
public final class NotificationQueryParams implements Immutable {
private final StartTimeParameter startTime;
--- /dev/null
+/*
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Parser and holder of query parameters from uriInfo for data and datastore modification operations.
+ */
+// FIXME: this should be a record with JDK17+
+public final class WriteDataParams implements Immutable {
+ private static final @NonNull WriteDataParams EMPTY = new WriteDataParams(null, null);
+
+ private final PointParameter point;
+ private final InsertParameter insert;
+
+ private WriteDataParams(final InsertParameter insert, final PointParameter point) {
+ this.insert = insert;
+ this.point = point;
+ }
+
+ public static @NonNull WriteDataParams empty() {
+ return EMPTY;
+ }
+
+ public static @NonNull WriteDataParams of(final InsertParameter insert, final PointParameter point) {
+ if (point == null) {
+ if (insert == null) {
+ return empty();
+ }
+
+ // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.5:
+ // If the values "before" or "after" are used, then a "point" query
+ // parameter for the "insert" query parameter MUST also be present, or a
+ // "400 Bad Request" status-line is returned.
+ if (insert == InsertParameter.BEFORE || insert == InsertParameter.AFTER) {
+ throw new IllegalArgumentException(
+ "Insert parameter " + insert.uriValue() + " cannot be used without a Point parameter.");
+ }
+ } else {
+ // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.6:
+ // [when "point" parameter is present and]
+ // If the "insert" query parameter is not present or has a value other
+ // than "before" or "after", then a "400 Bad Request" status-line is
+ // returned.
+ if (insert != InsertParameter.BEFORE && insert != InsertParameter.AFTER) {
+ throw new IllegalArgumentException(
+ "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+ }
+ }
+
+ return new WriteDataParams(insert, point);
+ }
+
+ public @Nullable InsertParameter insert() {
+ return insert;
+ }
+
+ public @Nullable PointParameter point() {
+ return point;
+ }
+
+ @Override
+ public String toString() {
+ final var helper = MoreObjects.toStringHelper(this).omitNullValues();
+ if (insert != null) {
+ helper.add("insert", insert.uriValue());
+ }
+ if (point != null) {
+ helper.add("point", point.value());
+ }
+ return helper.toString();
+ }
+}
\ No newline at end of file
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.nb.rfc8040.FilterParameter;
+import org.opendaylight.restconf.nb.rfc8040.InsertParameter;
import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
+import org.opendaylight.restconf.nb.rfc8040.PointParameter;
import org.opendaylight.restconf.nb.rfc8040.StartTimeParameter;
import org.opendaylight.restconf.nb.rfc8040.StopTimeParameter;
+import org.opendaylight.restconf.nb.rfc8040.WriteDataParams;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
@Beta
-public final class UriInfoSupport {
- private UriInfoSupport() {
+public final class QueryParams {
+ private QueryParams() {
// Utility class
}
}
}
+ public static @NonNull WriteDataParams newWriteDataParams(final UriInfo uriInfo) {
+ InsertParameter insert = null;
+ PointParameter point = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ final String uriName = entry.getKey();
+ final List<String> paramValues = entry.getValue();
+ if (uriName.equals(InsertParameter.uriName())) {
+ final String str = optionalParam(uriName, paramValues);
+ if (str != null) {
+ insert = InsertParameter.forUriValue(str);
+ if (insert == null) {
+ throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'",
+ ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
+ }
+ }
+ } else if (PointParameter.uriName().equals(uriName)) {
+ final String str = optionalParam(uriName, paramValues);
+ if (str != null) {
+ point = PointParameter.forUriValue(str);
+ }
+ } else {
+ throw new RestconfDocumentedException("Bad parameter for post: " + uriName,
+ ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
+ }
+ }
+
+ try {
+ return WriteDataParams.of(insert, point);
+ } catch (IllegalArgumentException e) {
+ throw new RestconfDocumentedException("Invalid query parameters: " + e.getMessage(), e);
+ }
+ }
+
public static @Nullable String getSingleParameter(final MultivaluedMap<String, String> params, final String name) {
final var values = params.get(name);
return values == null ? null : optionalParam(name, values);
}
- public static @Nullable String optionalParam(final String name, final List<String> values) {
+ private static @Nullable String optionalParam(final String name, final List<String> values) {
switch (values.size()) {
case 0:
return null;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
-import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMActionException;
import org.opendaylight.mdsal.dom.api.DOMActionResult;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.patch.PatchContext;
import org.opendaylight.restconf.common.patch.PatchStatusContext;
-import org.opendaylight.restconf.nb.rfc8040.InsertParameter;
-import org.opendaylight.restconf.nb.rfc8040.PointParameter;
import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
-import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.UriInfoSupport;
+import org.opendaylight.restconf.nb.rfc8040.WriteDataParams;
+import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
-import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
*/
@Path("/")
public class RestconfDataServiceImpl implements RestconfDataService {
- // FIXME: we should be able to interpret 'point' and refactor this class into a behavior
- private static final class QueryParams implements Immutable {
- final @Nullable PointParameter point;
- final @Nullable InsertParameter insert;
-
- QueryParams(final @Nullable InsertParameter insert, final @Nullable PointParameter point) {
- this.insert = insert;
- this.point = point;
- }
- }
-
private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
private static final QName NETCONF_BASE_QNAME = SchemaContext.NAME;
public Response putData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) {
requireNonNull(payload);
- final QueryParams checkedParms = checkQueryParameters(uriInfo);
+ final WriteDataParams checkedParms = QueryParams.newWriteDataParams(uriInfo);
final InstanceIdentifierContext<? extends SchemaNode> iid = payload.getInstanceIdentifierContext();
? schemaContextHandler.get() : modelContext(mountPoint);
final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
- return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert, checkedParms.point);
- }
-
- private static QueryParams checkQueryParameters(final UriInfo uriInfo) {
- InsertParameter insert = null;
- PointParameter point = null;
-
- for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
- final String uriName = entry.getKey();
- final List<String> paramValues = entry.getValue();
- if (uriName.equals(InsertParameter.uriName())) {
- final String str = UriInfoSupport.optionalParam(uriName, paramValues);
- if (str != null) {
- insert = InsertParameter.forUriValue(str);
- if (insert == null) {
- throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'",
- ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
- }
- }
- } else if (PointParameter.uriName().equals(uriName)) {
- final String str = UriInfoSupport.optionalParam(uriName, paramValues);
- if (str != null) {
- point = PointParameter.forUriValue(str);
- }
- } else {
- throw new RestconfDocumentedException("Bad parameter for post: " + uriName,
- ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
- }
- }
-
- // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.5:
- // If the values "before" or "after" are used, then a "point" query
- // parameter for the "insert" query parameter MUST also be present, or a
- // "400 Bad Request" status-line is returned.
- if ((insert == InsertParameter.BEFORE || insert == InsertParameter.AFTER) && point == null) {
- throw new RestconfDocumentedException(
- "Insert parameter " + insert.uriValue() + " cannot be used without a Point parameter.",
- ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
- }
- // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.6:
- // If the "insert" query parameter is not present or has a value other
- // than "before" or "after", then a "400 Bad Request" status-line is
- // returned.
- if (point != null && insert != InsertParameter.BEFORE && insert != InsertParameter.AFTER) {
- throw new RestconfDocumentedException(
- "Point parameter can be used only with 'after' or 'before' values of Insert parameter.",
- ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
- }
-
- return new QueryParams(insert, point);
+ return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert(), checkedParms.point());
}
@Override
return invokeAction(payload);
}
- final QueryParams checkedParms = checkQueryParameters(uriInfo);
+ final WriteDataParams checkedParms = QueryParams.newWriteDataParams(uriInfo);
final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
return PostDataTransactionUtil.postData(uriInfo, payload, strategy,
- getSchemaContext(mountPoint), checkedParms.insert, checkedParms.point);
+ getSchemaContext(mountPoint), checkedParms.insert(), checkedParms.point());
}
@Override
import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
-import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.UriInfoSupport;
+import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
@Override
public NormalizedNodePayload subscribeToStream(final String identifier, final UriInfo uriInfo) {
- final NotificationQueryParams notificationQueryParams = UriInfoSupport.newNotificationQueryParams(uriInfo);
+ final NotificationQueryParams params = QueryParams.newNotificationQueryParams(uriInfo);
final URI response;
if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCRIPTION)) {
- response = streamUtils.subscribeToDataStream(identifier, uriInfo, notificationQueryParams, handlersHolder);
+ response = streamUtils.subscribeToDataStream(identifier, uriInfo, params, handlersHolder);
} else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
- response = streamUtils.subscribeToYangStream(identifier, uriInfo, notificationQueryParams, handlersHolder);
+ response = streamUtils.subscribeToYangStream(identifier, uriInfo, params, handlersHolder);
} else {
final String msg = "Bad type of notification of sal-remote";
LOG.warn(msg);
*/
package org.opendaylight.restconf.nb.rfc8040.rests.utils;
-import static org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.UriInfoSupport.getSingleParameter;
+import static org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams.getSingleParameter;
import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter.parseFieldsParameter;
import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter.parseFieldsPaths;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
-public class UriInfoSupportTest {
+public class QueryParamsTest {
/**
* Test when parameter is present at most once.
*/
public void getSingleParameterTest() {
final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
parameters.putSingle(ContentParameter.uriName(), "all");
- assertEquals("all", UriInfoSupport.getSingleParameter(parameters, ContentParameter.uriName()));
+ assertEquals("all", QueryParams.getSingleParameter(parameters, ContentParameter.uriName()));
}
/**
parameters.put(ContentParameter.uriName(), List.of("config", "nonconfig", "all"));
final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
- () -> UriInfoSupport.getSingleParameter(parameters, ContentParameter.uriName()));
+ () -> QueryParams.getSingleParameter(parameters, ContentParameter.uriName()));
final List<RestconfError> errors = ex.getErrors();
assertEquals(1, errors.size());