/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.runtimeinfo.rest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.opencastproject.util.doc.rest.RestParameter;
import org.opencastproject.util.doc.rest.RestQuery;
import org.opencastproject.util.doc.rest.RestResponse;
import org.opencastproject.util.doc.rest.RestService;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* This test class tests the functionality of annotations used for documenting REST endpoints.
*/
public class RestDocsAnnotationTest {
/**
* This tests the functionality of @RestService annotation type.
*/
@Test
public void testRestServiceDocs() {
RestService restServiceAnnotation = TestServletSample.class.getAnnotation(RestService.class);
// name, title and abstract text
assertEquals("ingestservice", restServiceAnnotation.name());
assertEquals("Ingest Service", restServiceAnnotation.title());
assertEquals(
"This service creates and augments Matterhorn media packages that include media tracks, metadata catalogs and attachments.",
restServiceAnnotation.abstractText());
// notes
assertEquals(2, restServiceAnnotation.notes().length);
assertEquals("All paths above are relative to the REST endpoint base (something like http://your.server/files)",
restServiceAnnotation.notes()[0]);
assertEquals(
"If the service is down or not working it will return a status 503, this means the the underlying service is not working and is either restarting or has failed",
restServiceAnnotation.notes()[1]);
}
/**
* This tests the functionality of @RestQuery, @RestParameter, @RestResponse, @Path, @Produces, @Consumes annotation
* type.
*/
@Test
public void testRestQueryDocs() {
Method testMethod;
try {
testMethod = TestServletSample.class.getMethod("methodA");
if (testMethod != null) {
RestQuery restQueryAnnotation = (RestQuery) testMethod.getAnnotation(RestQuery.class);
Path pathAnnotation = (Path) testMethod.getAnnotation(Path.class);
Produces producesAnnotation = (Produces) testMethod.getAnnotation(Produces.class);
Consumes consumesAnnotation = (Consumes) testMethod.getAnnotation(Consumes.class);
assertEquals(1, producesAnnotation.value().length);
assertEquals(MediaType.TEXT_XML, producesAnnotation.value()[0]);
assertEquals(1, consumesAnnotation.value().length);
assertEquals(MediaType.MULTIPART_FORM_DATA, consumesAnnotation.value()[0]);
assertEquals("addTrack", pathAnnotation.value());
// we cannot hard code the GET.class or POST.class because we don't know which one is used.
for (Annotation a : testMethod.getAnnotations()) {
HttpMethod method = (HttpMethod) a.annotationType().getAnnotation(HttpMethod.class);
if (method != null) {
assertEquals("POST", a.annotationType().getSimpleName());
assertEquals("POST", method.value());
}
}
// name, description and return description
assertEquals("addTrackInputStream", restQueryAnnotation.name());
assertEquals("Add a media track to a given media package using an input stream",
restQueryAnnotation.description());
assertEquals("augmented media package", restQueryAnnotation.returnDescription());
// path parameter
assertTrue(restQueryAnnotation.pathParameters().length == 1);
assertEquals("wdID", restQueryAnnotation.pathParameters()[0].name());
assertEquals("Workflow definition id", restQueryAnnotation.pathParameters()[0].description());
assertTrue(restQueryAnnotation.pathParameters()[0].isRequired());
assertEquals("", restQueryAnnotation.pathParameters()[0].defaultValue());
assertEquals(RestParameter.Type.STRING, restQueryAnnotation.pathParameters()[0].type());
// query parameters
assertTrue(restQueryAnnotation.restParameters().length == 2);
// #1
assertEquals("flavor", restQueryAnnotation.restParameters()[0].name());
assertEquals("The kind of media track", restQueryAnnotation.restParameters()[0].description());
assertTrue(restQueryAnnotation.restParameters()[0].isRequired());
assertEquals("Default", restQueryAnnotation.restParameters()[0].defaultValue());
assertEquals(RestParameter.Type.STRING, restQueryAnnotation.restParameters()[0].type());
// #2
assertEquals("mediaPackage", restQueryAnnotation.restParameters()[1].name());
assertEquals("The media package as XML", restQueryAnnotation.restParameters()[1].description());
assertFalse(restQueryAnnotation.restParameters()[1].isRequired());
assertEquals("", restQueryAnnotation.restParameters()[1].defaultValue());
assertEquals(RestParameter.Type.TEXT, restQueryAnnotation.restParameters()[1].type());
// body parameter
assertEquals("BODY", restQueryAnnotation.bodyParameter().name());
assertEquals("The media track file", restQueryAnnotation.bodyParameter().description());
assertTrue(restQueryAnnotation.bodyParameter().isRequired());
assertEquals("", restQueryAnnotation.bodyParameter().defaultValue());
assertEquals(RestParameter.Type.FILE, restQueryAnnotation.bodyParameter().type());
// responses
assertTrue(restQueryAnnotation.reponses().length == 3);
assertEquals(HttpServletResponse.SC_OK, restQueryAnnotation.reponses()[0].responseCode());
assertEquals("Returns augmented media package", restQueryAnnotation.reponses()[0].description());
assertEquals(HttpServletResponse.SC_BAD_REQUEST, restQueryAnnotation.reponses()[1].responseCode());
assertEquals("", restQueryAnnotation.reponses()[1].description());
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, restQueryAnnotation.reponses()[2].responseCode());
assertEquals("", restQueryAnnotation.reponses()[2].description());
}
} catch (SecurityException e) {
fail();
} catch (NoSuchMethodException e) {
fail();
}
}
/**
* This tests the functionality of @RestQuery, @RestParameter, @RestResponse, @Path, @Produces, @Consumes annotation
* type using a different technique.
*/
@Test
public void testRestQueryDocs2() {
Method testMethod;
try {
testMethod = TestServletSample.class.getMethod("methodA");
if (testMethod != null) {
RestDocData restDocData = new RestDocData("NAME", "TITLE", "URL", null, new TestServletSample(),
new HashMap<String, String>());
RestQuery restQueryAnnotation = (RestQuery) testMethod.getAnnotation(RestQuery.class);
Path pathAnnotation = (Path) testMethod.getAnnotation(Path.class);
Produces producesAnnotation = (Produces) testMethod.getAnnotation(Produces.class);
String httpMethodString = null;
// we cannot hard code the GET.class or POST.class because we don't know which one is used.
for (Annotation a : testMethod.getAnnotations()) {
HttpMethod method = (HttpMethod) a.annotationType().getAnnotation(HttpMethod.class);
if (method != null) {
httpMethodString = method.value();
}
}
RestEndpointData endpoint = new RestEndpointData(testMethod.getReturnType(),
restDocData.processMacro(restQueryAnnotation.name()), httpMethodString, "/" + pathAnnotation.value(),
restDocData.processMacro(restQueryAnnotation.description()));
if (!restQueryAnnotation.returnDescription().isEmpty()) {
endpoint.addNote("Return value description: "
+ restDocData.processMacro(restQueryAnnotation.returnDescription()));
}
// name, description and return description
assertEquals("addTrackInputStream", endpoint.getName());
assertEquals("Add a media track to a given media package using an input stream", endpoint.getDescription());
assertEquals(1, endpoint.getNotes().size());
assertEquals("Return value description: augmented media package", endpoint.getNotes().get(0));
// HTTP method
assertEquals("POST", endpoint.getMethod());
assertEquals("/addTrack", endpoint.getPath());
// @Produces
if (producesAnnotation != null) {
for (String format : producesAnnotation.value()) {
endpoint.addFormat(new RestFormatData(format));
}
}
assertEquals(1, endpoint.getFormats().size());
assertEquals(MediaType.TEXT_XML, endpoint.getFormats().get(0).getName());
// responses
for (RestResponse restResp : restQueryAnnotation.reponses()) {
endpoint.addStatus(restResp, restDocData);
}
assertEquals(3, endpoint.getStatuses().size());
assertEquals(HttpServletResponse.SC_OK, endpoint.getStatuses().get(0).getCode());
assertEquals("Returns augmented media package", endpoint.getStatuses().get(0).getDescription());
assertEquals(HttpServletResponse.SC_BAD_REQUEST, endpoint.getStatuses().get(1).getCode());
assertEquals(null, endpoint.getStatuses().get(1).getDescription());
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, endpoint.getStatuses().get(2).getCode());
assertEquals(null, endpoint.getStatuses().get(2).getDescription());
// body parameter
if (restQueryAnnotation.bodyParameter().type() != RestParameter.Type.NO_PARAMETER) {
endpoint.addBodyParam(restQueryAnnotation.bodyParameter(), restDocData);
}
assertEquals("BODY", endpoint.getBodyParam().getName());
assertEquals("The media track file", endpoint.getBodyParam().getDescription());
assertTrue(endpoint.getBodyParam().isRequired());
assertEquals(null, endpoint.getBodyParam().getDefaultValue());
assertTrue("FILE".equalsIgnoreCase(endpoint.getBodyParam().getType()));
// path parameter
for (RestParameter restParam : restQueryAnnotation.pathParameters()) {
endpoint.addPathParam(new RestParamData(restParam, restDocData));
}
assertEquals(1, endpoint.getPathParams().size());
assertEquals("wdID", endpoint.getPathParams().get(0).getName());
assertEquals("Workflow definition id", endpoint.getPathParams().get(0).getDescription());
assertTrue(endpoint.getPathParams().get(0).isRequired());
assertTrue(endpoint.getPathParams().get(0).isPath());
assertEquals(null, endpoint.getPathParams().get(0).getDefaultValue());
assertTrue("STRING".equalsIgnoreCase(endpoint.getPathParams().get(0).getType()));
// query parameters
for (RestParameter restParam : restQueryAnnotation.restParameters()) {
if (restParam.isRequired()) {
endpoint.addRequiredParam(new RestParamData(restParam, restDocData));
} else {
endpoint.addOptionalParam(new RestParamData(restParam, restDocData));
}
}
// #1
assertEquals(1, endpoint.getRequiredParams().size());
assertEquals("flavor", endpoint.getRequiredParams().get(0).getName());
assertEquals("The kind of media track", endpoint.getRequiredParams().get(0).getDescription());
assertTrue(endpoint.getRequiredParams().get(0).isRequired());
assertEquals("Default", endpoint.getRequiredParams().get(0).getDefaultValue());
assertTrue("STRING".equalsIgnoreCase(endpoint.getRequiredParams().get(0).getType()));
// #2
assertEquals(1, endpoint.getOptionalParams().size());
assertEquals("mediaPackage", endpoint.getOptionalParams().get(0).getName());
assertEquals("The media package as XML", endpoint.getOptionalParams().get(0).getDescription());
assertFalse(endpoint.getOptionalParams().get(0).isRequired());
assertEquals(null, endpoint.getOptionalParams().get(0).getDefaultValue());
assertTrue("TEXT".equalsIgnoreCase(endpoint.getOptionalParams().get(0).getType()));
}
} catch (SecurityException e) {
fail();
} catch (NoSuchMethodException e) {
fail();
}
}
@Test
public void testRestQueryDocsMacros() {
Method testMethod = null;
Map<String, String> map = new HashMap<String, String>();
map.put("somethingElse", "the value of something else");
map.put("anotherthing", "the value of another thing");
try {
testMethod = TestServletSample.class.getMethod("methodB");
if (testMethod != null) {
RestQuery restQueryAnnotation = (RestQuery) testMethod.getAnnotation(RestQuery.class);
RestDocData restDocData = new RestDocData("NAME", "TITLE", "URL", null, new TestServletSample(), map);
assertEquals(restDocData.processMacro(restQueryAnnotation.restParameters()[1].defaultValue()),
"ADCD THIS IS SCHEMA XUHZSUFH the value of something else UGGUH the value of another thing AIHID");
}
} catch (SecurityException e) {
fail();
} catch (NoSuchMethodException e) {
e.printStackTrace();
fail();
}
}
@Test
public void testPathPatternMatching() throws Exception {
assertTrue("/{seriesID:.+}".matches(RestDocData.PATH_VALIDATION_REGEX));
}
/**
* This sample class simulates a annotated REST service class.
*/
@RestService(name = "ingestservice", title = "Ingest Service", notes = {
"All paths above are relative to the REST endpoint base (something like http://your.server/files)",
"If the service is down or not working it will return a status 503, this means the the underlying service is not working and is either restarting or has failed" }, abstractText = "This service creates and augments Matterhorn media packages that include media tracks, metadata catalogs and attachments.")
public class TestServletSample {
@POST
@Produces(MediaType.TEXT_XML)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("addTrack")
@RestQuery(name = "addTrackInputStream", description = "Add a media track to a given media package using an input stream", pathParameters = { @RestParameter(defaultValue = "", description = "Workflow definition id", isRequired = true, name = "wdID", type = RestParameter.Type.STRING) }, restParameters = {
@RestParameter(defaultValue = "Default", description = "The kind of media track", isRequired = true, name = "flavor", type = RestParameter.Type.STRING),
@RestParameter(defaultValue = "", description = "The media package as XML", isRequired = false, name = "mediaPackage", type = RestParameter.Type.TEXT) }, bodyParameter = @RestParameter(defaultValue = "", description = "The media track file", isRequired = true, name = "BODY", type = RestParameter.Type.FILE), reponses = {
@RestResponse(description = "Returns augmented media package", responseCode = HttpServletResponse.SC_OK),
@RestResponse(description = "", responseCode = HttpServletResponse.SC_BAD_REQUEST),
@RestResponse(description = "", responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR) }, returnDescription = "augmented media package")
public int methodA() {
return 0;
}
@POST
@Produces(MediaType.TEXT_XML)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("addTrack")
@RestQuery(name = "addTrackInputStream", description = "Add a media track to a given media package using an input stream", pathParameters = { @RestParameter(defaultValue = "", description = "Workflow definition id", isRequired = true, name = "wdID", type = RestParameter.Type.STRING) }, restParameters = {
@RestParameter(defaultValue = "Default", description = "The kind of media track", isRequired = true, name = "flavor", type = RestParameter.Type.STRING),
@RestParameter(defaultValue = "ADCD ${this.schema} XUHZSUFH ${somethingElse} UGGUH ${anotherthing} AIHID", description = "The media package as XML", isRequired = false, name = "mediaPackage", type = RestParameter.Type.TEXT) }, bodyParameter = @RestParameter(defaultValue = "", description = "The media track file", isRequired = true, name = "BODY", type = RestParameter.Type.FILE), reponses = {
@RestResponse(description = "Returns augmented media package", responseCode = HttpServletResponse.SC_OK),
@RestResponse(description = "", responseCode = HttpServletResponse.SC_BAD_REQUEST),
@RestResponse(description = "", responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR) }, returnDescription = "augmented media package")
public int methodB() {
return 0;
}
public String getSchema() {
return "THIS IS SCHEMA";
}
}
}