1 /*
2 * Licensed to The Apereo Foundation under one or more contributor license
3 * agreements. See the NOTICE file distributed with this work for additional
4 * information regarding copyright ownership.
5 *
6 *
7 * The Apereo Foundation licenses this file to you under the Educational
8 * Community License, Version 2.0 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of the License
10 * at:
11 *
12 * http://opensource.org/licenses/ecl2.txt
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 * License for the specific language governing permissions and limitations under
18 * the License.
19 *
20 */
21
22 package org.opencastproject.runtimeinfo.rest;
23
24 import static org.apache.commons.lang3.StringUtils.isBlank;
25
26 import org.opencastproject.util.JaxbXmlSchemaGenerator;
27 import org.opencastproject.util.doc.DocData;
28 import org.opencastproject.util.doc.rest.RestParameter;
29 import org.opencastproject.util.doc.rest.RestResponse;
30
31 import org.apache.commons.lang3.StringEscapeUtils;
32
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Vector;
36
37 public class RestEndpointData implements Comparable<RestEndpointData> {
38
39 /**
40 * The name of the endpoint, which should be unique. In the documentation page, the same type of endpoints are shown
41 * in ascending order of name.
42 */
43 private String name;
44
45 /**
46 * The HTTP method used to invoke the endpoint.
47 */
48 private String httpMethod;
49
50 /**
51 * The path for this endpoint (e.g. /search OR /add/{id}).
52 */
53 private String path;
54
55 /**
56 * The description of this endpoint.
57 */
58 private String description;
59
60 /**
61 * The body parameter of this endpoint.
62 */
63 private RestParamData bodyParam;
64
65 /**
66 * The list of path parameters of this endpoint.
67 */
68 private List<RestParamData> pathParams;
69
70 /**
71 * The list of required query parameters of this endpoint.
72 */
73 private List<RestParamData> requiredParams;
74
75 /**
76 * The list of optional query parameters of this endpoint.
77 */
78 private List<RestParamData> optionalParams;
79
80 /**
81 * The list of notes (i.e. extra information) of this endpoint.
82 */
83 private List<String> notes;
84
85 /**
86 * The list of formats returned by this endpoint.
87 */
88 private List<RestFormatData> formats;
89
90 /**
91 * The list of HTTP responses returned by this endpoint.
92 */
93 private List<StatusData> statuses;
94
95 /**
96 * The form for testing this endpoint in the documentation page.
97 */
98 private RestFormData form;
99
100 /** The XML schema for data returned by this endpoint. */
101 private String returnTypeSchema = null;
102
103 /**
104 * Create a new basic endpoint, you should use the add methods to fill in the rest of the information about the
105 * endpoint data
106 *
107 * @param returnType
108 * the endpoint's return type
109 * @param name
110 * the endpoint's name (this should be unique in the same type of endpoints)
111 * @param httpMethod
112 * the HTTP method used for this endpoint
113 * @param path
114 * the path for this endpoint (e.g. /search OR /add/{id})
115 * @param description
116 * [optional] the description of this endpoint
117 * @throws IllegalArgumentException
118 * if name is null, name is not alphanumeric, method is null, path is null or path is not valid.
119 */
120 public RestEndpointData(Class<?> returnType, String name, String httpMethod, String path, String description)
121 throws IllegalArgumentException {
122 if (!DocData.isValidName(name)) {
123 throw new IllegalArgumentException("Name must not be null and must be alphanumeric.");
124 }
125 if ((httpMethod == null) || (httpMethod.isEmpty())) {
126 throw new IllegalArgumentException("Method must not be null and must not be empty.");
127 }
128 if (!RestDocData.isValidPath(path)) {
129 throw new IllegalArgumentException(String.format("Path '%s' must not be null and must look something like "
130 + "/a/b/{c}.", path));
131 }
132 this.returnTypeSchema = JaxbXmlSchemaGenerator.getXmlSchema(returnType);
133 this.name = name;
134 this.httpMethod = httpMethod.toUpperCase();
135 this.path = path;
136 this.description = description;
137 }
138
139 /**
140 * Returns a string representation of this object.
141 *
142 * @return a string representation of this object
143 */
144 @Override
145 public String toString() {
146 return "ENDP:" + name + ":" + httpMethod + " " + path + " :body=" + bodyParam + " :req=" + requiredParams
147 + " :opt=" + optionalParams + " :formats=" + formats + " :status=" + statuses + " :form=" + form;
148 }
149
150 /**
151 * Adds a body parameter to this endpoint. Once added, the body parameter becomes a required parameter.
152 *
153 * @param restParam
154 * a RestParameter annotation object corresponding to the body parameter
155 *
156 * @return the new RestParamData object in case you want to set attributes
157 */
158 public RestParamData addBodyParam(RestParameter restParam) {
159 RestParamData.Type type = RestParamData.Type.valueOf(restParam.type().name());
160 RestParamData param = new RestParamData("BODY", type, restParam.defaultValue(),
161 restParam.description(), null);
162 param.setRequired(true);
163 bodyParam = param;
164 return param;
165 }
166
167 /**
168 * Adds a path parameter for this endpoint, this would be a parameter which is passed as part of the path (e.g.
169 * /my/path/{param}) and thus must use a name which is safe to be placed in a URL and does not contain a slash (/)
170 *
171 * @param param
172 * the path parameter to add
173 * @throws IllegalStateException
174 * if the type of the path parameter is FILE or TEXT
175 */
176 public void addPathParam(RestParamData param) throws IllegalStateException {
177 if (RestParamData.Type.FILE.name().equals(param.getType())
178 || RestParamData.Type.TEXT.name().equals(param.getType())) {
179 throw new IllegalStateException("Cannot add path param of type FILE or TEXT.");
180 }
181 param.setRequired(true);
182 param.setPath(true);
183 if (pathParams == null) {
184 pathParams = new Vector<RestParamData>(3);
185 }
186 pathParams.add(param);
187 }
188
189 /**
190 * Adds a required form parameter for this endpoint, this would be a parameter which is passed encoded as part of the
191 * request body (commonly referred to as a post or form parameter). <br>
192 * WARNING: This should generally be reserved for endpoints which are used for processing, it is better to use path
193 * params unless the required parameter is not part of an identifier for the resource.
194 *
195 * @param param
196 * the required parameter to add
197 */
198 public void addRequiredParam(RestParamData param) throws IllegalStateException {
199 param.setRequired(true);
200 param.setPath(false);
201 if (requiredParams == null) {
202 requiredParams = new Vector<RestParamData>(3);
203 }
204 requiredParams.add(param);
205 }
206
207 /**
208 * Adds an optional parameter for this endpoint, this would be a parameter which is passed in the query string (for
209 * GET) or encoded as part of the body otherwise (often referred to as a post or form parameter).
210 *
211 * @param param
212 * the optional parameter to add
213 */
214 public void addOptionalParam(RestParamData param) {
215 param.setRequired(false);
216 param.setPath(false);
217 if (optionalParams == null) {
218 optionalParams = new Vector<RestParamData>(3);
219 }
220 optionalParams.add(param);
221 }
222
223 /**
224 * Adds a format for the return data for this endpoint.
225 *
226 * @param format
227 * a RestFormatData object
228 */
229 public void addFormat(RestFormatData format) {
230 if (formats == null) {
231 formats = new Vector<RestFormatData>(2);
232 }
233 formats.add(format);
234 }
235
236 /**
237 * Adds a response status for this endpoint.
238 *
239 * @param restResponse
240 * a RestResponse object containing the HTTP response code and description
241 */
242 public void addStatus(RestResponse restResponse) {
243 if (statuses == null) {
244 statuses = new Vector<StatusData>(3);
245 }
246 statuses.add(new StatusData(restResponse));
247 }
248
249 /**
250 * Adds a note for this endpoint.
251 *
252 * @param note
253 * a string providing more information about this endpoint
254 * @throws IllegalArgumentException
255 * if note is blank (e.g. null, empty string)
256 */
257 public void addNote(String note) throws IllegalArgumentException {
258 if (isBlank(note)) {
259 throw new IllegalArgumentException("Note must not be null or blank.");
260 }
261 if (notes == null) {
262 notes = new Vector<String>(3);
263 }
264 notes.add(note);
265 }
266
267 /**
268 * Sets the test form for this endpoint, if this is null then no test form is rendered for this endpoint.
269 *
270 * @param form
271 * the test form object (null to clear the form)
272 */
273 public void setTestForm(RestFormData form) {
274 this.form = form;
275 }
276
277 /**
278 * Returns whether this endpoint's HTTP method is GET
279 *
280 * @return true if this endpoint method is GET, otherwise false
281 */
282 public boolean isGetMethod() {
283 return "GET".equals(httpMethod);
284 }
285
286 /**
287 * Returns the URL-encoded query string for a GET endpoint.
288 *
289 * @return the calculated query string for a GET endpoint (e.g. ?blah=1), will be urlencoded for html display
290 */
291 public String getQueryString() {
292 String qs = "";
293 if (isGetMethod()) {
294 if (optionalParams != null && !optionalParams.isEmpty()) {
295 StringBuilder sb = new StringBuilder();
296 sb.append("?");
297 for (RestParamData p : optionalParams) {
298 if (sb.length() > 2) {
299 sb.append("&");
300 }
301 sb.append(p.getName());
302 sb.append("=");
303 if (p.getDefaultValue() != null) {
304 sb.append(p.getDefaultValue());
305 } else {
306 sb.append("{");
307 sb.append(p.getName());
308 sb.append("}");
309 }
310 }
311 qs = StringEscapeUtils.escapeHtml4(sb.toString());
312 }
313 }
314 return qs;
315 }
316
317 /**
318 * Gets the name of this endpoint.
319 *
320 * @return the name of this endpoint
321 */
322 public String getName() {
323 return name;
324 }
325
326 /**
327 * Gets the name of HTTP method used to invoke this endpoint.
328 *
329 * @return the name of HTTP method used to invoke this endpoint
330 */
331 public String getMethod() {
332 return httpMethod;
333 }
334
335 /**
336 * Gets the path for this endpoint.
337 *
338 * @return the path for this endpoint
339 */
340 public String getPath() {
341 return path;
342 }
343
344 /**
345 * Gets the description of this endpoint.
346 *
347 * @return the description of this endpoint
348 */
349 public String getDescription() {
350 return description;
351 }
352
353 /**
354 * Gets the body parameter of this endpoint.
355 *
356 * @return the body parameter of this endpoint
357 */
358 public RestParamData getBodyParam() {
359 return bodyParam;
360 }
361
362 /**
363 * Gets the list of path parameters of this endpoint.
364 *
365 * @return the list of path parameters of this endpoint
366 */
367 public List<RestParamData> getPathParams() {
368 if (pathParams == null) {
369 pathParams = new ArrayList<RestParamData>(0);
370 }
371 return pathParams;
372 }
373
374 /**
375 * Gets the list of required parameters of this endpoint.
376 *
377 * @return the list of required parameters of this endpoint
378 */
379 public List<RestParamData> getRequiredParams() {
380 if (requiredParams == null) {
381 requiredParams = new ArrayList<RestParamData>(0);
382 }
383 return requiredParams;
384 }
385
386 /**
387 * Gets the list of optional parameters of this endpoint.
388 *
389 * @return list of optional parameters of this endpoint
390 */
391 public List<RestParamData> getOptionalParams() {
392 if (optionalParams == null) {
393 optionalParams = new ArrayList<RestParamData>(0);
394 }
395 return optionalParams;
396 }
397
398 /**
399 * Gets the list of formats returned by this endpoint.
400 *
401 * @return the list of formats returned by this endpoint
402 */
403 public List<RestFormatData> getFormats() {
404 if (formats == null) {
405 formats = new ArrayList<RestFormatData>(0);
406 }
407 return formats;
408 }
409
410 /**
411 * Gets the list of HTTP responses returned by this endpoint.
412 *
413 * @return the list of HTTP responses returned by this endpoint
414 */
415 public List<StatusData> getStatuses() {
416 if (statuses == null) {
417 statuses = new ArrayList<StatusData>(0);
418 }
419 return statuses;
420 }
421
422 /**
423 * Gets list of notes (i.e. extra information) of this endpoint.
424 *
425 * @return the list of notes (i.e. extra information) of this endpoint
426 */
427 public List<String> getNotes() {
428 if (notes == null) {
429 notes = new ArrayList<>(0);
430 }
431 return notes;
432 }
433
434 /**
435 * Gets the form for testing this endpoint in the documentation page.
436 *
437 * @return the form for testing this endpoint in the documentation page
438 */
439 public RestFormData getForm() {
440 return form;
441 }
442
443 /**
444 * Compares two RestEndpointData by their names so that the list of endpoints can be sorted.
445 *
446 * @param otherEndpoint
447 * the other endpoint object to compare to
448 *
449 * @return a negative integer, zero, or a positive integer as the name of the supplied endpoint is greater than, equal
450 * to, or less than this endpoint, ignoring case considerations.
451 */
452 @Override
453 public int compareTo(RestEndpointData otherEndpoint) {
454 return name.compareToIgnoreCase(otherEndpoint.name);
455 }
456
457 /**
458 * @return the XML schema for this endpoint's return type
459 */
460 public String getReturnTypeSchema() {
461 return returnTypeSchema;
462 }
463
464 public String getEscapedReturnTypeSchema() {
465 return StringEscapeUtils.escapeXml(returnTypeSchema);
466 }
467
468 }