View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.rest.annotation;
18  
19  import static org.junit.jupiter.api.Assertions.*;
20  
21  import java.util.*;
22  
23  import org.apache.juneau.*;
24  import org.apache.juneau.http.annotation.*;
25  import org.apache.juneau.json.*;
26  import org.apache.juneau.rest.mock.*;
27  import org.apache.juneau.testutils.pojos.*;
28  import org.junit.jupiter.api.*;
29  
30  class PathRemainder_Test extends TestBase {
31  
32  	//------------------------------------------------------------------------------------------------------------------
33  	// Simple tests
34  	//------------------------------------------------------------------------------------------------------------------
35  
36  	@Rest
37  	public static class A  {
38  		@RestOp(path="/a/*")
39  		public String a(@Path("/*") String remainder) {
40  			return ""+remainder;
41  		}
42  		@RestGet(path="/b/*")
43  		public String b(@Path("/*") String remainder) {
44  			return ""+remainder;
45  		}
46  		@RestPut(path="/c/*")
47  		public String c(@Path("/*") String remainder) {
48  			return ""+remainder;
49  		}
50  		@RestPost(path="/d/*")
51  		public String d(@Path("/*") String remainder) {
52  			return ""+remainder;
53  		}
54  		@RestDelete(path="/e/*")
55  		public String e(@Path("/*") String remainder) {
56  			return ""+remainder;
57  		}
58  	}
59  
60  	@Test void a01_basic() throws Exception {
61  		var a = MockRestClient.build(A.class);
62  
63  		a.get("/a").run().assertContent("null");
64  		a.get("/a/").run().assertContent("");
65  		a.get("/a/foo").run().assertContent("foo");
66  		a.get("/a/foo/bar").run().assertContent("foo/bar");
67  
68  		a.get("/b").run().assertContent("null");
69  		a.get("/b/").run().assertContent("");
70  		a.get("/b/foo").run().assertContent("foo");
71  		a.get("/b/foo/bar").run().assertContent("foo/bar");
72  
73  		a.put("/c").run().assertContent("null");
74  		a.put("/c/").run().assertContent("");
75  		a.put("/c/foo").run().assertContent("foo");
76  		a.put("/c/foo/bar").run().assertContent("foo/bar");
77  
78  		a.post("/d").run().assertContent("null");
79  		a.post("/d/").run().assertContent("");
80  		a.post("/d/foo").run().assertContent("foo");
81  		a.post("/d/foo/bar").run().assertContent("foo/bar");
82  
83  		a.delete("/e").run().assertContent("null");
84  		a.delete("/e/").run().assertContent("");
85  		a.delete("/e/foo").run().assertContent("foo");
86  		a.delete("/e/foo/bar").run().assertContent("foo/bar");
87  	}
88  
89  	//------------------------------------------------------------------------------------------------------------------
90  	// Optional path remainder parameter.
91  	//------------------------------------------------------------------------------------------------------------------
92  
93  	@Rest(serializers=Json5Serializer.class)
94  	public static class B {
95  		@RestGet(path="/a/*")
96  		public Object a(@Path("/*") Optional<Integer> f1) {
97  			assertNotNull(f1);
98  			return f1;
99  		}
100 		@RestPut(path="/b/*")
101 		public Object b(@Path("/*") Optional<ABean> f1) {
102 			assertNotNull(f1);
103 			return f1;
104 		}
105 		@RestPost(path="/c/*")
106 		public Object c(@Path("/*") Optional<List<ABean>> f1) {
107 			assertNotNull(f1);
108 			return f1;
109 		}
110 		@RestDelete(path="/d/*")
111 		public Object d(@Path("/*") List<Optional<ABean>> f1) {
112 			return f1;
113 		}
114 	}
115 
116 	@Test void b01_optionalParam() throws Exception {
117 		var b = MockRestClient.buildJson(B.class);
118 		b.get("/a/123")
119 			.run()
120 			.assertStatus(200)
121 			.assertContent("123");
122 		b.put("/b/a=1,b=foo")
123 			.run()
124 			.assertStatus(200)
125 			.assertContent("{a:1,b:'foo'}");
126 		b.post("/c/@((a=1,b=foo))")
127 			.run()
128 			.assertStatus(200)
129 			.assertContent("[{a:1,b:'foo'}]");
130 		b.delete("/d/@((a=1,b=foo))")
131 			.run()
132 			.assertStatus(200)
133 			.assertContent("[{a:1,b:'foo'}]");
134 	}
135 
136 	//------------------------------------------------------------------------------------------------------------------
137 	// @PathRemainder annotation tests
138 	//------------------------------------------------------------------------------------------------------------------
139 
140 	@Rest
141 	public static class C {
142 		@RestOp(path="/a/*")
143 		public String a(@PathRemainder String remainder) {
144 			return ""+remainder;
145 		}
146 		@RestGet(path="/b/*")
147 		public String b(@PathRemainder String remainder) {
148 			return ""+remainder;
149 		}
150 		@RestPut(path="/c/*")
151 		public String c(@PathRemainder String remainder) {
152 			return ""+remainder;
153 		}
154 		@RestPost(path="/d/*")
155 		public String d(@PathRemainder String remainder) {
156 			return ""+remainder;
157 		}
158 		@RestDelete(path="/e/*")
159 		public String e(@PathRemainder String remainder) {
160 			return ""+remainder;
161 		}
162 	}
163 
164 	@Test void c01_pathRemainderAnnotation() throws Exception {
165 		var c = MockRestClient.build(C.class);
166 
167 		// Test that @PathRemainder works identically to @Path("/*")
168 		c.get("/a").run().assertContent("null");
169 		c.get("/a/").run().assertContent("");
170 		c.get("/a/foo").run().assertContent("foo");
171 		c.get("/a/foo/bar").run().assertContent("foo/bar");
172 
173 		c.get("/b").run().assertContent("null");
174 		c.get("/b/").run().assertContent("");
175 		c.get("/b/foo").run().assertContent("foo");
176 		c.get("/b/foo/bar").run().assertContent("foo/bar");
177 
178 		c.put("/c").run().assertContent("null");
179 		c.put("/c/").run().assertContent("");
180 		c.put("/c/foo").run().assertContent("foo");
181 		c.put("/c/foo/bar").run().assertContent("foo/bar");
182 
183 		c.post("/d").run().assertContent("null");
184 		c.post("/d/").run().assertContent("");
185 		c.post("/d/foo").run().assertContent("foo");
186 		c.post("/d/foo/bar").run().assertContent("foo/bar");
187 
188 		c.delete("/e").run().assertContent("null");
189 		c.delete("/e/").run().assertContent("");
190 		c.delete("/e/foo").run().assertContent("foo");
191 		c.delete("/e/foo/bar").run().assertContent("foo/bar");
192 	}
193 
194 	//------------------------------------------------------------------------------------------------------------------
195 	// @PathRemainder with Optional and complex types
196 	//------------------------------------------------------------------------------------------------------------------
197 
198 	@Rest(serializers=Json5Serializer.class)
199 	public static class D {
200 		@RestGet(path="/a/*")
201 		public Object a(@PathRemainder Optional<Integer> f1) {
202 			assertNotNull(f1);
203 			return f1;
204 		}
205 		@RestPut(path="/b/*")
206 		public Object b(@PathRemainder Optional<ABean> f1) {
207 			assertNotNull(f1);
208 			return f1;
209 		}
210 		@RestPost(path="/c/*")
211 		public Object c(@PathRemainder Optional<List<ABean>> f1) {
212 			assertNotNull(f1);
213 			return f1;
214 		}
215 		@RestDelete(path="/d/*")
216 		public Object d(@PathRemainder List<Optional<ABean>> f1) {
217 			return f1;
218 		}
219 	}
220 
221 	@Test void d01_pathRemainderWithOptional() throws Exception {
222 		var d = MockRestClient.buildJson(D.class);
223 		d.get("/a/123")
224 			.run()
225 			.assertStatus(200)
226 			.assertContent("123");
227 		d.put("/b/a=1,b=foo")
228 			.run()
229 			.assertStatus(200)
230 			.assertContent("{a:1,b:'foo'}");
231 		d.post("/c/@((a=1,b=foo))")
232 			.run()
233 			.assertStatus(200)
234 			.assertContent("[{a:1,b:'foo'}]");
235 		d.delete("/d/@((a=1,b=foo))")
236 			.run()
237 			.assertStatus(200)
238 			.assertContent("[{a:1,b:'foo'}]");
239 	}
240 
241 	//------------------------------------------------------------------------------------------------------------------
242 	// @PathRemainder with mixed path parameters
243 	//------------------------------------------------------------------------------------------------------------------
244 
245 	@Rest
246 	public static class E {
247 		@RestGet(path="/a/{foo}/{bar}/*")
248 		public String a(@Path("foo") String foo, @Path("bar") int bar, @PathRemainder String remainder) {
249 			return "foo="+foo+",bar="+bar+",remainder="+remainder;
250 		}
251 		@RestPost(path="/b/{id}/*")
252 		public String b(@Path("id") String id, @PathRemainder String remainder) {
253 			return "id="+id+",remainder="+remainder;
254 		}
255 	}
256 
257 	@Test void e01_pathRemainderWithOtherPathParams() throws Exception {
258 		var e = MockRestClient.build(E.class);
259 		e.get("/a/x/123/extra/path")
260 			.run()
261 			.assertContent("foo=x,bar=123,remainder=extra/path");
262 		e.get("/a/hello/456")
263 			.run()
264 			.assertContent("foo=hello,bar=456,remainder=null");
265 		e.post("/b/myId/more/stuff")
266 			.run()
267 			.assertContent("id=myId,remainder=more/stuff");
268 	}
269 
270 	//------------------------------------------------------------------------------------------------------------------
271 	// @PathRemainder with default values
272 	//------------------------------------------------------------------------------------------------------------------
273 
274 	@Rest
275 	public static class F {
276 		@RestGet(path="/a/*")
277 		public String a(@PathRemainder(def="defaultValue") String remainder) {
278 			return ""+remainder;
279 		}
280 	}
281 
282 	@Test void f01_pathRemainderWithDefault() throws Exception {
283 		var f = MockRestClient.build(F.class);
284 		f.get("/a").run().assertContent("defaultValue");
285 		f.get("/a/").run().assertContent("");
286 		f.get("/a/custom").run().assertContent("custom");
287 	}
288 }