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 org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertThrows;
15 import static org.junit.Assert.assertTrue;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.withSettings;
20 import java.util.List;
22 import java.util.Optional;
24 import java.util.function.Function;
25 import javax.ws.rs.core.MultivaluedHashMap;
26 import javax.ws.rs.core.MultivaluedMap;
27 import javax.ws.rs.core.UriInfo;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.mockito.junit.MockitoJUnitRunner;
31 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
32 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
33 import org.opendaylight.restconf.common.errors.RestconfError;
34 import org.opendaylight.restconf.nb.rfc8040.ContentParam;
35 import org.opendaylight.restconf.nb.rfc8040.DepthParam;
36 import org.opendaylight.restconf.nb.rfc8040.InsertParam;
37 import org.opendaylight.restconf.nb.rfc8040.RestconfQueryParam;
38 import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParam;
39 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
40 import org.opendaylight.yangtools.yang.common.ErrorTag;
41 import org.opendaylight.yangtools.yang.common.ErrorType;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
45 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
47 import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
48 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
50 @RunWith(MockitoJUnitRunner.StrictStubs.class)
51 public class QueryParamsTest {
53 * Test when parameter is present at most once.
56 public void optionalParamTest() {
57 assertEquals("all", QueryParams.optionalParam(ContentParam.uriName, List.of("all")));
61 * Test when parameter is present more than once.
64 public void optionalParamMultipleTest() {
65 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
66 () -> QueryParams.optionalParam(ContentParam.uriName, List.of("config", "nonconfig", "all")));
67 final List<RestconfError> errors = ex.getErrors();
68 assertEquals(1, errors.size());
70 final RestconfError error = errors.get(0);
71 assertEquals("Error type is not correct", ErrorType.PROTOCOL, error.getErrorType());
72 assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, error.getErrorTag());
76 * Test when not allowed parameter type is used.
79 public void checkParametersTypesNegativeTest() {
80 assertUnknownParam(QueryParams::newNotificationQueryParams);
81 assertUnknownParam(QueryParams::newReadDataParams);
82 assertUnknownParam(QueryParams::newWriteDataParams);
84 assertInvalidParam(QueryParams::newNotificationQueryParams, ContentParam.ALL);
85 assertInvalidParam(QueryParams::newReadDataParams, InsertParam.LAST);
86 assertInvalidParam(QueryParams::newWriteDataParams, ContentParam.ALL);
90 * Test of parsing default parameters from URI request.
93 public void parseUriParametersDefaultTest() {
94 // no parameters, default values should be used
95 final var params = assertParams(QueryParams::newReadDataParams, new MultivaluedHashMap<String, String>());
96 assertEquals(ContentParam.ALL, params.content());
97 assertNull(params.depth());
98 assertNull(params.fields());
102 public void testInvalidValueReadDataParams() {
103 assertInvalidValue(QueryParams::newReadDataParams, ContentParam.uriName);
104 assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName);
105 assertInvalidValue(QueryParams::newReadDataParams, WithDefaultsParam.uriName);
107 // inserted value is too high
108 assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "65536");
109 // inserted value is too low
110 assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "0");
114 * Testing parsing of with-defaults parameter which value matches 'report-all-tagged' setting - default value should
115 * be set to {@code null} and tagged flag should be set to {@code true}.
118 public void parseUriParametersWithDefaultAndTaggedTest() {
119 final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all-tagged");
120 assertNull(params.withDefaults());
121 assertTrue(params.tagged());
125 * Testing parsing of with-defaults parameter which value matches 'report-all' setting - default value should
126 * be set to {@code null} and tagged flag should be set to {@code false}.
129 public void parseUriParametersWithDefaultAndReportAllTest() {
130 final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all");
131 assertNull(params.withDefaults());
132 assertFalse(params.tagged());
136 * Testing parsing of with-defaults parameter which value doesn't match report-all or report-all-tagged patterns
137 * - non-reporting setting.
140 public void parseUriParametersWithDefaultAndNonTaggedTest() {
141 final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "explicit");
142 assertEquals(WithDefaultsParam.EXPLICIT, params.withDefaults());
143 assertFalse(params.tagged());
147 * Test of parsing user defined parameters from URI request.
150 public void parseUriParametersUserDefinedTest() {
151 final QName containerChild = QName.create("ns", "container-child");
153 final var parameters = new MultivaluedHashMap<String, String>();
154 parameters.putSingle("content", "config");
155 parameters.putSingle("depth", "10");
156 parameters.putSingle("fields", "container-child");
158 final var params = assertParams(QueryParams::newReadDataParams, parameters);
160 assertEquals(ContentParam.CONFIG, params.content());
163 final DepthParam depth = params.depth();
164 assertNotNull(depth);
165 assertEquals(10, depth.value());
168 assertNotNull(params.fields());
170 // fields for write filtering
171 final var containerSchema = mock(ContainerSchemaNode.class,
172 withSettings().extraInterfaces(ContainerEffectiveStatement.class));
173 final var containerQName = QName.create(containerChild, "container");
174 doReturn(containerQName).when(containerSchema).getQName();
175 doReturn(SchemaPath.create(true, containerQName)).when(containerSchema).getPath();
176 final var containerChildSchema = mock(LeafSchemaNode.class);
177 doReturn(containerChild).when(containerChildSchema).getQName();
178 doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild);
180 final var module = mock(ModuleEffectiveStatement.class);
181 doReturn(Optional.of(containerSchema)).when(module).findSchemaTreeNode(containerQName);
182 final var context = mock(EffectiveModelContext.class);
183 doReturn(Map.of(containerQName.getModule(), module)).when(context).getModuleStatements();
185 final QueryParameters queryParameters = QueryParams.newQueryParameters(params,
186 InstanceIdentifierContext.ofDataSchemaNode(context, containerSchema));
187 final List<Set<QName>> fields = queryParameters.fields();
188 assertNotNull(fields);
189 assertEquals(1, fields.size());
190 assertEquals(Set.of(containerChild), fields.get(0));
193 private static void assertInvalidParam(final Function<UriInfo, ?> paramsMethod, final RestconfQueryParam<?> param) {
194 final var params = new MultivaluedHashMap<String, String>();
195 params.putSingle(param.paramName(), "odl-test-value");
196 assertParamsThrows(ErrorTag.MALFORMED_MESSAGE, paramsMethod, params);
199 private static void assertUnknownParam(final Function<UriInfo, ?> paramsMethod) {
200 final var params = new MultivaluedHashMap<String, String>();
201 params.putSingle("odl-unknown-param", "odl-test-value");
202 assertParamsThrows(ErrorTag.UNKNOWN_ATTRIBUTE, paramsMethod, params);
205 private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name) {
206 assertInvalidValue(paramsMethod, name, "odl-invalid-value");
209 private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name,
210 final String value) {
211 final var params = new MultivaluedHashMap<String, String>();
212 params.putSingle(name, value);
213 assertParamsThrows(ErrorTag.INVALID_VALUE, paramsMethod, params);
216 private static void assertParamsThrows(final ErrorTag expectedTag, final Function<UriInfo, ?> paramsMethod,
217 final MultivaluedMap<String, String> params) {
218 final var uriInfo = mock(UriInfo.class);
219 doReturn(params).when(uriInfo).getQueryParameters();
221 final var ex = assertThrows(RestconfDocumentedException.class, () -> paramsMethod.apply(uriInfo));
222 final var errors = ex.getErrors();
223 assertEquals(1, errors.size());
225 final var error = errors.get(0);
226 assertEquals(ErrorType.PROTOCOL, error.getErrorType());
227 assertEquals(expectedTag, error.getErrorTag());
230 private static <T> T assertParams(final Function<UriInfo, T> paramsMethod, final String name,
231 final String value) {
232 final var params = new MultivaluedHashMap<String, String>();
233 params.putSingle(name, value);
234 return assertParams(paramsMethod, params);
237 private static <T> T assertParams(final Function<UriInfo, T> paramsMethod,
238 final MultivaluedMap<String, String> params) {
239 final var uriInfo = mock(UriInfo.class);
240 doReturn(params).when(uriInfo).getQueryParameters();
241 return paramsMethod.apply(uriInfo);