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.assertNotNull;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertThrows;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.withSettings;
18 import java.util.List;
20 import java.util.Optional;
22 import java.util.function.Function;
23 import javax.ws.rs.core.MultivaluedHashMap;
24 import javax.ws.rs.core.MultivaluedMap;
25 import javax.ws.rs.core.UriInfo;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.mockito.junit.MockitoJUnitRunner;
29 import org.opendaylight.restconf.api.query.ContentParam;
30 import org.opendaylight.restconf.api.query.DepthParam;
31 import org.opendaylight.restconf.api.query.InsertParam;
32 import org.opendaylight.restconf.api.query.RestconfQueryParam;
33 import org.opendaylight.restconf.api.query.WithDefaultsParam;
34 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
35 import org.opendaylight.restconf.common.errors.RestconfError;
36 import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
37 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
38 import org.opendaylight.yangtools.yang.common.ErrorTag;
39 import org.opendaylight.yangtools.yang.common.ErrorType;
40 import org.opendaylight.yangtools.yang.common.QName;
41 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
43 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
45 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
46 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
48 @RunWith(MockitoJUnitRunner.StrictStubs.class)
49 public class QueryParamsTest {
51 * Test when parameter is present at most once.
54 public void optionalParamTest() {
55 assertEquals("all", QueryParams.optionalParam(ContentParam.uriName, List.of("all")));
59 * Test when parameter is present more than once.
62 public void optionalParamMultipleTest() {
63 final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
64 () -> QueryParams.optionalParam(ContentParam.uriName, List.of("config", "nonconfig", "all")));
65 final List<RestconfError> errors = ex.getErrors();
66 assertEquals(1, errors.size());
68 final RestconfError error = errors.get(0);
69 assertEquals("Error type is not correct", ErrorType.PROTOCOL, error.getErrorType());
70 assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, error.getErrorTag());
74 * Test when not allowed parameter type is used.
77 public void checkParametersTypesNegativeTest() {
78 assertUnknownParam(QueryParams::newNotificationQueryParams);
79 assertUnknownParam(QueryParams::newReadDataParams);
80 assertUnknownParam(QueryParams::parseInsert);
82 assertInvalidParam(QueryParams::newNotificationQueryParams, ContentParam.ALL);
83 assertInvalidParam(QueryParams::newReadDataParams, InsertParam.LAST);
84 assertInvalidParam(QueryParams::parseInsert, ContentParam.ALL);
88 * Test of parsing default parameters from URI request.
91 public void parseUriParametersDefaultTest() {
92 // no parameters, default values should be used
93 final var params = assertParams(QueryParams::newReadDataParams, new MultivaluedHashMap<>());
94 assertEquals(ContentParam.ALL, params.content());
95 assertNull(params.depth());
96 assertNull(params.fields());
100 public void testInvalidValueReadDataParams() {
101 assertInvalidValue(QueryParams::newReadDataParams, ContentParam.uriName);
102 assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName);
103 assertInvalidValue(QueryParams::newReadDataParams, WithDefaultsParam.uriName);
105 // inserted value is too high
106 assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "65536");
107 // inserted value is too low
108 assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "0");
112 * Testing parsing of with-defaults parameter which value matches 'report-all-tagged' setting.
115 public void parseUriParametersWithDefaultAndTaggedTest() {
116 final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all-tagged");
117 assertEquals(WithDefaultsParam.REPORT_ALL_TAGGED, params.withDefaults());
121 * Testing parsing of with-defaults parameter which value matches 'report-all' setting.
124 public void parseUriParametersWithDefaultAndReportAllTest() {
125 final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all");
126 assertEquals(WithDefaultsParam.REPORT_ALL, params.withDefaults());
130 * Testing parsing of with-defaults parameter which value doesn't match report-all or report-all-tagged patterns
131 * - non-reporting setting.
134 public void parseUriParametersWithDefaultAndNonTaggedTest() {
135 final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "explicit");
136 assertEquals(WithDefaultsParam.EXPLICIT, params.withDefaults());
140 * Test of parsing user defined parameters from URI request.
143 public void parseUriParametersUserDefinedTest() {
144 final QName containerChild = QName.create("ns", "container-child");
146 final var parameters = new MultivaluedHashMap<String, String>();
147 parameters.putSingle("content", "config");
148 parameters.putSingle("depth", "10");
149 parameters.putSingle("fields", "container-child");
151 final var params = assertParams(QueryParams::newReadDataParams, parameters);
153 assertEquals(ContentParam.CONFIG, params.content());
156 final DepthParam depth = params.depth();
157 assertNotNull(depth);
158 assertEquals(10, depth.value());
161 assertNotNull(params.fields());
163 // fields for write filtering
164 final var containerSchema = mock(ContainerSchemaNode.class,
165 withSettings().extraInterfaces(ContainerEffectiveStatement.class));
166 final var containerQName = QName.create(containerChild, "container");
167 doReturn(containerQName).when(containerSchema).getQName();
168 final var containerChildSchema = mock(LeafSchemaNode.class);
169 doReturn(containerChild).when(containerChildSchema).getQName();
170 doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild);
172 final var module = mock(ModuleEffectiveStatement.class);
173 doReturn(Optional.of(containerSchema)).when(module).findSchemaTreeNode(containerQName);
174 final var context = mock(EffectiveModelContext.class);
175 doReturn(Map.of(containerQName.getModule(), module)).when(context).getModuleStatements();
177 final var stack = SchemaInferenceStack.of(context);
178 stack.enterSchemaTree(containerQName);
180 final QueryParameters queryParameters = QueryParams.newQueryParameters(params,
181 InstanceIdentifierContext.ofStack(stack));
182 final List<Set<QName>> fields = queryParameters.fields();
183 assertNotNull(fields);
184 assertEquals(1, fields.size());
185 assertEquals(Set.of(containerChild), fields.get(0));
188 private static void assertInvalidParam(final Function<UriInfo, ?> paramsMethod, final RestconfQueryParam<?> param) {
189 final var params = new MultivaluedHashMap<String, String>();
190 params.putSingle(param.paramName(), "odl-test-value");
191 assertParamsThrows(ErrorTag.MALFORMED_MESSAGE, paramsMethod, params);
194 private static void assertUnknownParam(final Function<UriInfo, ?> paramsMethod) {
195 final var params = new MultivaluedHashMap<String, String>();
196 params.putSingle("odl-unknown-param", "odl-test-value");
197 assertParamsThrows(ErrorTag.UNKNOWN_ATTRIBUTE, paramsMethod, params);
200 private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name) {
201 assertInvalidValue(paramsMethod, name, "odl-invalid-value");
204 private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name,
205 final String value) {
206 final var params = new MultivaluedHashMap<String, String>();
207 params.putSingle(name, value);
208 assertParamsThrows(ErrorTag.INVALID_VALUE, paramsMethod, params);
211 private static void assertParamsThrows(final ErrorTag expectedTag, final Function<UriInfo, ?> paramsMethod,
212 final MultivaluedMap<String, String> params) {
213 final var uriInfo = mock(UriInfo.class);
214 doReturn(params).when(uriInfo).getQueryParameters();
216 final var ex = assertThrows(RestconfDocumentedException.class, () -> paramsMethod.apply(uriInfo));
217 final var errors = ex.getErrors();
218 assertEquals(1, errors.size());
220 final var error = errors.get(0);
221 assertEquals(ErrorType.PROTOCOL, error.getErrorType());
222 assertEquals(expectedTag, error.getErrorTag());
225 private static <T> T assertParams(final Function<UriInfo, T> paramsMethod, final String name,
226 final String value) {
227 final var params = new MultivaluedHashMap<String, String>();
228 params.putSingle(name, value);
229 return assertParams(paramsMethod, params);
232 private static <T> T assertParams(final Function<UriInfo, T> paramsMethod,
233 final MultivaluedMap<String, String> params) {
234 final var uriInfo = mock(UriInfo.class);
235 doReturn(params).when(uriInfo).getQueryParameters();
236 return paramsMethod.apply(uriInfo);