Add RestconfQueryParam
[netconf.git] / restconf / restconf-nb-rfc8040 / src / test / java / org / opendaylight / restconf / nb / rfc8040 / FieldsParameterTest.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.nb.rfc8040;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
12
13 import java.text.ParseException;
14 import java.util.List;
15 import org.junit.Test;
16 import org.opendaylight.restconf.nb.rfc8040.ApiPath.ApiIdentifier;
17 import org.opendaylight.restconf.nb.rfc8040.FieldsParam.NodeSelector;
18
19 public class FieldsParameterTest {
20     // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.3:
21     //    ";" is used to select multiple nodes.  For example, to retrieve only
22     //    the "genre" and "year" of an album, use "fields=genre;year".
23     @Test
24     public void testGenreYear() {
25         final var selectors = assertValidFields("genre;year");
26         assertEquals(2, selectors.size());
27
28         var selector = selectors.get(0);
29         assertEquals(List.of(new ApiIdentifier(null, "genre")), selector.path());
30         assertEquals(List.of(), selector.subSelectors());
31
32         selector = selectors.get(1);
33         assertEquals(List.of(new ApiIdentifier(null, "year")), selector.path());
34         assertEquals(List.of(), selector.subSelectors());
35     }
36
37     // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.3:
38     //    "/" is used in a path to retrieve a child node of a node.  For
39     //    example, to retrieve only the "label" of an album, use
40     //    "fields=admin/label".
41     @Test
42     public void testAdminLabel() throws ParseException {
43         final var selectors = assertValidFields("admin/label");
44         assertEquals(1, selectors.size());
45
46         final var selector = selectors.get(0);
47         assertEquals(List.of(new ApiIdentifier(null, "admin"), new ApiIdentifier(null, "label")), selector.path());
48         assertEquals(List.of(), selector.subSelectors());
49     }
50
51     // https://datatracker.ietf.org/doc/html/rfc8040#section-4.8.3:
52     //    For example, assume that the target resource is the "album" list.  To
53     //    retrieve only the "label" and "catalogue-number" of the "admin"
54     //    container within an album, use
55     //    "fields=admin(label;catalogue-number)".
56     @Test
57     public void testAdminLabelCatalogueNumber() throws ParseException {
58         final var selectors = assertValidFields("admin(label;catalogue-number)");
59         assertEquals(1, selectors.size());
60
61         final var selector = selectors.get(0);
62         assertEquals(List.of(new ApiIdentifier(null, "admin")), selector.path());
63
64         final var subSelectors = selector.subSelectors();
65         assertEquals(2, subSelectors.size());
66
67         var subSelector = subSelectors.get(0);
68         assertEquals(List.of(new ApiIdentifier(null, "label")), subSelector.path());
69         assertEquals(List.of(), subSelector.subSelectors());
70
71
72         subSelector = subSelectors.get(1);
73         assertEquals(List.of(new ApiIdentifier(null, "catalogue-number")), subSelector.path());
74         assertEquals(List.of(), subSelector.subSelectors());
75     }
76
77     // https://datatracker.ietf.org/doc/html/rfc8040#appendix-B.3.3:
78     //    In this example, the client is retrieving the datastore resource in
79     //    JSON format, but retrieving only the "modules-state/module" list, and
80     //    only the "name" and "revision" nodes from each list entry.  Note that
81     //    the top node returned by the server matches the target resource node
82     //    (which is "data" in this example).  The "module-set-id" leaf is not
83     //    returned because it is not selected in the fields expression.
84     //
85     //       GET /restconf/data?fields=ietf-yang-library:modules-state/\
86     //           module(name;revision) HTTP/1.1
87     @Test
88     public void testModulesModuleNameRevision() {
89         final var selectors = assertValidFields("ietf-yang-library:modules-state/module(name;revision)");
90         assertEquals(1, selectors.size());
91
92         final var selector = selectors.get(0);
93         assertEquals(
94             List.of(new ApiIdentifier("ietf-yang-library", "modules-state"), new ApiIdentifier(null, "module")),
95             selector.path());
96
97         final var subSelectors = selector.subSelectors();
98         assertEquals(2, subSelectors.size());
99
100         var subSelector = subSelectors.get(0);
101         assertEquals(List.of(new ApiIdentifier(null, "name")), subSelector.path());
102         assertEquals(List.of(), subSelector.subSelectors());
103
104         subSelector = subSelectors.get(1);
105         assertEquals(List.of(new ApiIdentifier(null, "revision")), subSelector.path());
106         assertEquals(List.of(), subSelector.subSelectors());
107     }
108
109     @Test
110     public void testModulesSimple() {
111         final var selectors = assertValidFields("ietf-yang-library:modules-state");
112         assertEquals(1, selectors.size());
113
114         final var selector = selectors.get(0);
115         assertEquals(List.of(new ApiIdentifier("ietf-yang-library", "modules-state")), selector.path());
116         assertEquals(List.of(), selector.subSelectors());
117     }
118
119     @Test
120     public void testUnqualifiedSubQualified() {
121         final var selectors = assertValidFields("a(b:c)");
122         assertEquals(1, selectors.size());
123
124         final var selector = selectors.get(0);
125         assertEquals(List.of(new ApiIdentifier(null, "a")), selector.path());
126
127         final var subSelectors = selector.subSelectors();
128         assertEquals(1, subSelectors.size());
129
130         final var subSelector = subSelectors.get(0);
131         assertEquals(List.of(new ApiIdentifier("b", "c")), subSelector.path());
132         assertEquals(List.of(), subSelector.subSelectors());
133     }
134
135     @Test
136     public void testQualifiedSubUnqualified() {
137         final var selectors = assertValidFields("a:b(c)");
138         assertEquals(1, selectors.size());
139
140         final var selector = selectors.get(0);
141         assertEquals(List.of(new ApiIdentifier("a", "b")), selector.path());
142
143         final var subSelectors = selector.subSelectors();
144         assertEquals(1, subSelectors.size());
145
146         final var subSelector = subSelectors.get(0);
147         assertEquals(List.of(new ApiIdentifier(null, "c")), subSelector.path());
148         assertEquals(List.of(), subSelector.subSelectors());
149     }
150
151     @Test
152     public void testDeepNesting() {
153         final var selectors = assertValidFields("a(b(c(d)));e(f(g(h)));i(j(k(l)))");
154         assertEquals(3, selectors.size());
155     }
156
157     @Test
158     public void testInvalidIdentifier() {
159         assertInvalidFields(".", "Expecting [a-ZA-Z_], not '.'", 0);
160         assertInvalidFields("a+", "Expecting [a-zA-Z_.-/(:;], not '+'", 1);
161         assertInvalidFields("a:.", "Expecting [a-ZA-Z_], not '.'", 2);
162         assertInvalidFields("a:b+", "Expecting [a-zA-Z_.-/(:;], not '+'", 3);
163         assertInvalidFields("a;)", "Expecting [a-ZA-Z_], not ')'", 2);
164     }
165
166     @Test
167     public void testUnexpectedEnds() {
168         assertInvalidFields("a;", "Unexpected end of input", 2);
169         assertInvalidFields("a(", "Unexpected end of input", 2);
170         assertInvalidFields("a(a", "Unexpected end of input", 3);
171     }
172
173     @Test
174     public void testUnexpectedRightParent() {
175         assertInvalidFields("a)", "Expecting ';', not ')'", 1);
176     }
177
178     private static void assertInvalidFields(final String str, final String message, final int errorOffset) {
179         final var ex = assertThrows(ParseException.class, () -> FieldsParam.parse(str));
180         assertEquals(message, ex.getMessage());
181         assertEquals(errorOffset, ex.getErrorOffset());
182     }
183
184     private static List<NodeSelector> assertValidFields(final String str) {
185         try {
186             return FieldsParam.parse(str).nodeSelectors();
187         } catch (ParseException e) {
188             throw new AssertionError(e);
189         }
190     }
191 }