2 * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.databind.jaxrs;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.annotations.VisibleForTesting;
14 import java.util.List;
15 import java.util.Map.Entry;
17 import java.util.function.Function;
18 import javax.ws.rs.core.UriInfo;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
22 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
23 import org.opendaylight.restconf.common.errors.RestconfError;
24 import org.opendaylight.restconf.nb.rfc8040.ContentParam;
25 import org.opendaylight.restconf.nb.rfc8040.DepthParam;
26 import org.opendaylight.restconf.nb.rfc8040.FieldsParam;
27 import org.opendaylight.restconf.nb.rfc8040.FilterParam;
28 import org.opendaylight.restconf.nb.rfc8040.InsertParam;
29 import org.opendaylight.restconf.nb.rfc8040.LeafNodesOnlyParam;
30 import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
31 import org.opendaylight.restconf.nb.rfc8040.PointParam;
32 import org.opendaylight.restconf.nb.rfc8040.PrettyPrintParam;
33 import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
34 import org.opendaylight.restconf.nb.rfc8040.SkipNotificationDataParam;
35 import org.opendaylight.restconf.nb.rfc8040.StartTimeParam;
36 import org.opendaylight.restconf.nb.rfc8040.StopTimeParam;
37 import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParam;
38 import org.opendaylight.restconf.nb.rfc8040.WriteDataParams;
39 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
40 import org.opendaylight.restconf.nb.rfc8040.utils.parser.NetconfFieldsTranslator;
41 import org.opendaylight.restconf.nb.rfc8040.utils.parser.WriterFieldsTranslator;
42 import org.opendaylight.yangtools.yang.common.ErrorTag;
43 import org.opendaylight.yangtools.yang.common.ErrorType;
46 public final class QueryParams {
47 private static final Set<String> KNOWN_PARAMS = Set.of(
49 ContentParam.uriName, DepthParam.uriName, FieldsParam.uriName, WithDefaultsParam.uriName,
50 PrettyPrintParam.uriName,
52 InsertParam.uriName, PointParam.uriName,
54 FilterParam.uriName, StartTimeParam.uriName, StopTimeParam.uriName,
55 LeafNodesOnlyParam.uriName, SkipNotificationDataParam.uriName);
57 private QueryParams() {
61 public static @NonNull NotificationQueryParams newNotificationQueryParams(final UriInfo uriInfo) {
62 StartTimeParam startTime = null;
63 StopTimeParam stopTime = null;
64 FilterParam filter = null;
65 LeafNodesOnlyParam leafNodesOnly = null;
66 SkipNotificationDataParam skipNotificationData = null;
68 for (Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
69 final String paramName = entry.getKey();
70 final List<String> paramValues = entry.getValue();
74 case FilterParam.uriName:
75 filter = optionalParam(FilterParam::forUriValue, paramName, paramValues);
77 case StartTimeParam.uriName:
78 startTime = optionalParam(StartTimeParam::forUriValue, paramName, paramValues);
80 case StopTimeParam.uriName:
81 stopTime = optionalParam(StopTimeParam::forUriValue, paramName, paramValues);
83 case LeafNodesOnlyParam.uriName:
84 leafNodesOnly = optionalParam(LeafNodesOnlyParam::forUriValue, paramName, paramValues);
86 case SkipNotificationDataParam.uriName:
87 skipNotificationData = optionalParam(SkipNotificationDataParam::forUriValue, paramName,
91 throw unhandledParam("notification", paramName);
93 } catch (IllegalArgumentException e) {
94 throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(),
95 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e);
100 return NotificationQueryParams.of(startTime, stopTime, filter, leafNodesOnly, skipNotificationData);
101 } catch (IllegalArgumentException e) {
102 throw new RestconfDocumentedException("Invalid query parameters: " + e.getMessage(), e);
106 public static QueryParameters newQueryParameters(final ReadDataParams params,
107 final InstanceIdentifierContext identifier) {
108 final var fields = params.fields();
109 if (fields == null) {
110 return QueryParameters.of(params);
113 return identifier.getMountPoint() != null
114 ? QueryParameters.ofFieldPaths(params, NetconfFieldsTranslator.translate(identifier, fields))
115 : QueryParameters.ofFields(params, WriterFieldsTranslator.translate(identifier, fields));
119 * Parse parameters from URI request and check their types and values.
121 * @param uriInfo URI info
122 * @return {@link ReadDataParams}
124 public static @NonNull ReadDataParams newReadDataParams(final UriInfo uriInfo) {
125 ContentParam content = ContentParam.ALL;
126 DepthParam depth = null;
127 FieldsParam fields = null;
128 WithDefaultsParam withDefaults = null;
129 PrettyPrintParam prettyPrint = null;
130 boolean tagged = false;
132 for (Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
133 final String paramName = entry.getKey();
134 final List<String> paramValues = entry.getValue();
138 case ContentParam.uriName:
139 content = optionalParam(ContentParam::forUriValue, paramName, paramValues);
141 case DepthParam.uriName:
142 final String depthStr = optionalParam(paramName, paramValues);
144 depth = DepthParam.forUriValue(depthStr);
145 } catch (IllegalArgumentException e) {
146 throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
147 ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depthStr, null,
148 "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
151 case FieldsParam.uriName:
152 fields = optionalParam(FieldsParam::forUriValue, paramName, paramValues);
154 case WithDefaultsParam.uriName:
155 final var defaultsVal = optionalParam(WithDefaultsParam::forUriValue, paramName, paramValues);
156 if (defaultsVal != null) {
157 switch (defaultsVal) {
162 case REPORT_ALL_TAGGED:
167 withDefaults = defaultsVal;
172 case PrettyPrintParam.uriName:
173 prettyPrint = optionalParam(PrettyPrintParam::forUriValue, paramName, paramValues);
176 throw unhandledParam("read", paramName);
178 } catch (IllegalArgumentException e) {
179 throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(),
180 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e);
184 return ReadDataParams.of(content, depth, fields, withDefaults, tagged, prettyPrint);
187 public static @NonNull WriteDataParams newWriteDataParams(final UriInfo uriInfo) {
188 InsertParam insert = null;
189 PointParam point = null;
191 for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
192 final String paramName = entry.getKey();
193 final List<String> paramValues = entry.getValue();
197 case InsertParam.uriName:
198 insert = optionalParam(InsertParam::forUriValue, paramName, paramValues);
200 case PointParam.uriName:
201 point = optionalParam(PointParam::forUriValue, paramName, paramValues);
204 throw unhandledParam("write", paramName);
206 } catch (IllegalArgumentException e) {
207 throw new RestconfDocumentedException("Invalid " + paramName + " value: " + e.getMessage(),
208 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e);
213 return WriteDataParams.of(insert, point);
214 } catch (IllegalArgumentException e) {
215 throw new RestconfDocumentedException("Invalid query parameters: " + e.getMessage(), e);
219 private static RestconfDocumentedException unhandledParam(final String operation, final String name) {
220 return KNOWN_PARAMS.contains(name)
221 ? new RestconfDocumentedException("Invalid parameter in " + operation + ": " + name,
222 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE)
223 : new RestconfDocumentedException("Unknown parameter in " + operation + ": " + name,
224 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE);
228 static @Nullable String optionalParam(final String name, final List<String> values) {
229 switch (values.size()) {
233 return requireNonNull(values.get(0));
235 throw new RestconfDocumentedException("Parameter " + name + " can appear at most once in request URI",
236 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
240 private static <T> @Nullable T optionalParam(final Function<String, @NonNull T> factory, final String name,
241 final List<String> values) {
242 final String str = optionalParam(name, values);
243 return str == null ? null : factory.apply(str);