View Javadoc
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 }