LtiServiceRestEndpoint.java
/*
* 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.lti.service.endpoint;
import static org.opencastproject.util.doc.rest.RestParameter.Type.STRING;
import org.opencastproject.lti.service.api.LtiFileUpload;
import org.opencastproject.lti.service.api.LtiJob;
import org.opencastproject.lti.service.api.LtiService;
import org.opencastproject.security.api.UnauthorizedException;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.doc.rest.RestParameter;
import org.opencastproject.util.doc.rest.RestParameter.Type;
import org.opencastproject.util.doc.rest.RestQuery;
import org.opencastproject.util.doc.rest.RestResponse;
import org.opencastproject.util.doc.rest.RestService;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
/**
* The REST endpoint for the remote LTI service (for multi-node setups with LTI)
*/
@Path("/lti-service")
@RestService(
name = "ltirestservice",
title = "LTI Service",
notes = {},
abstractText = "Provides operations to LTI clients"
)
@Component(
immediate = true,
service = LtiServiceRestEndpoint.class,
property = {
"service.description=LTI Service",
"opencast.service.type=org.opencastproject.lti.service",
"opencast.service.path=/lti-service"
}
)
@JaxrsResource
public class LtiServiceRestEndpoint {
private static final Gson gson = new Gson();
/* OSGi service references */
private LtiService service;
/** OSGi DI */
@Reference
public void setService(LtiService service) {
this.service = service;
}
@GET
@Path("/jobs")
@Produces(MediaType.APPLICATION_JSON)
@RestQuery(
name = "listjobs",
description = "List recent jobs for a specific series.",
returnDescription = "",
restParameters = {
@RestParameter(
name = "seriesId",
description = "The id of the series you want jobs for",
isRequired = true,
type = STRING
),
},
responses = {
@RestResponse(description = "The list of jobs", responseCode = HttpServletResponse.SC_OK),
}
)
public Response listJobs(@QueryParam("seriesId") String seriesId) {
return Response.status(Status.OK)
.entity(gson.toJson(service.listJobs(seriesId), new TypeToken<List<LtiJob>>() {
}.getType())).build();
}
@POST
@Path("/")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@RestQuery(
name = "createevent",
description = "Creates an event by sending metadata in a multipart request.",
returnDescription = "",
restParameters = {
@RestParameter(
name = "metadata",
description = "Event metadata",
isRequired = true,
type = STRING
),
@RestParameter(
name = "presenter",
description = "Presenter movie track",
isRequired = true,
type = Type.FILE
),
@RestParameter(
name = "captions",
description = "Caption file",
isRequired = false,
type = STRING
),
@RestParameter(
name = "captionFormat",
description = "Caption file format",
isRequired = false,
type = STRING
),
@RestParameter(
name = "captionLanguage",
description = "Caption language",
isRequired = false,
type = STRING
),
@RestParameter(
name = "isPartOf",
description = "Series id of the event",
isRequired = false,
type = STRING
),
@RestParameter(
name = "eventId",
description = "ID of the event to update (if it's an update)",
isRequired = false,
type = STRING
)
},
responses = {
@RestResponse(
description = "A new event is created or the event is updated",
responseCode = HttpServletResponse.SC_OK
),
@RestResponse(
description = "No authorization to create or update events",
responseCode = HttpServletResponse.SC_UNAUTHORIZED
),
@RestResponse(
description = "The event to be updated wasn't found",
responseCode = HttpServletResponse.SC_NOT_FOUND
)
}
)
public Response createNewEvent(@HeaderParam("Accept") String acceptHeader, @Context HttpServletRequest request) {
String seriesId = "";
try {
String captions = null;
String captionFormat = null;
String captionLanguage = null;
String eventId = null;
String metadataJson = null;
for (FileItemIterator iter = new ServletFileUpload().getItemIterator(request); iter.hasNext();) {
final FileItemStream item = iter.next();
final String fieldName = item.getFieldName();
if ("isPartOf".equals(fieldName)) {
final String fieldValue = Streams.asString(item.openStream());
if (!fieldValue.isEmpty()) {
seriesId = fieldValue;
}
} else if ("metadata".equals(fieldName)) {
metadataJson = Streams.asString(item.openStream());
} else if ("captions".equals(fieldName)) {
captions = Streams.asString(item.openStream());
} else if ("captionFormat".equals(fieldName)) {
captionFormat = Streams.asString(item.openStream());
} else if ("captionLanguage".equals(fieldName)) {
captionLanguage = Streams.asString(item.openStream());
} else if ("eventId".equals(fieldName)) {
eventId = Streams.asString(item.openStream());
} else {
final InputStream stream = item.openStream();
final String streamName = item.getName();
service.upsertEvent(
new LtiFileUpload(stream, streamName),
captions,
captionFormat,
captionLanguage,
eventId,
seriesId,
metadataJson);
return Response.ok().build();
}
}
if (eventId == null) {
return Response.status(Status.BAD_REQUEST).entity("No file given").build();
}
service.upsertEvent(null, captions, captionFormat, captionLanguage, eventId, seriesId, metadataJson);
return Response.ok().build();
} catch (FileUploadException | IOException e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("error while uploading").build();
} catch (NotFoundException e) {
return Response.status(Status.NOT_FOUND).build();
} catch (UnauthorizedException e) {
return Response.status(Status.UNAUTHORIZED).build();
}
}
@POST
@Path("{eventId}/copy")
@RestQuery(
name = "copyeventtoseries",
description = "Copy an event to a different series",
returnDescription = "",
pathParameters = {
@RestParameter(
name = "eventId",
description = "The event (id) to copy",
isRequired = true,
type = STRING)
},
restParameters = {
@RestParameter(
name = "seriesId",
description = "The series (id) to copy into",
isRequired = true,
type = STRING
)
},
responses = {
@RestResponse(
description = "The event has been copied.",
responseCode = HttpServletResponse.SC_NO_CONTENT
),
@RestResponse(
description = "The specified event does not exist.",
responseCode = HttpServletResponse.SC_NOT_FOUND
)
}
)
public Response copyEventToSeries(@PathParam("eventId") final String eventId,
@QueryParam("seriesId") final String seriesId) {
service.copyEventToSeries(eventId, seriesId);
return Response.noContent().build();
}
@POST
@Path("{eventId}/metadata")
@Produces(MediaType.APPLICATION_JSON)
@RestQuery(
name = "seteventmetadata",
description = "Set the metadata of an existing event",
returnDescription = "",
pathParameters = {
@RestParameter(name = "eventId", description = "The event id", isRequired = true, type = STRING)
},
restParameters = {
@RestParameter(name = "metadataJson", description = "Event metadata", isRequired = true, type = STRING),
},
responses = {
@RestResponse(
description = "The event's metadata has been set",
responseCode = HttpServletResponse.SC_OK
),
@RestResponse(
description = "The event doesn't exist",
responseCode = HttpServletResponse.SC_NOT_FOUND
),
@RestResponse(
description = "The event cannot be accessed",
responseCode = HttpServletResponse.SC_UNAUTHORIZED
),
}
)
public Response setEventMetadataJson(
@PathParam("eventId") final String eventId,
@FormParam("metadataJson") final String metadataJson
) {
try {
this.service.setEventMetadataJson(eventId, metadataJson);
return Response.ok().build();
} catch (NotFoundException e) {
return Response.status(Status.NOT_FOUND).build();
} catch (UnauthorizedException e) {
return Response.status(Status.UNAUTHORIZED).build();
}
}
@GET
@Path("new/metadata")
@Produces(MediaType.APPLICATION_JSON)
@RestQuery(
name = "getneweventmetadata",
description = "Get the metadata of a new event",
returnDescription = "The metadata of a new event",
responses = {
@RestResponse(description = "A new event's metadata", responseCode = HttpServletResponse.SC_OK),
}
)
public Response getNewEventMetadata() {
return Response.ok(service.getNewEventMetadata(), MediaType.APPLICATION_JSON).build();
}
@GET
@Path("{eventId}/metadata")
@Produces(MediaType.APPLICATION_JSON)
@RestQuery(
name = "geteventmetadata",
description = "Get the metadata of an existing event",
returnDescription = "The metadata of an existing event",
pathParameters = {
@RestParameter(name = "eventId", description = "The event id", isRequired = true, type = STRING)
},
responses = {
@RestResponse(
description = "Metadata is available and will be returned",
responseCode = HttpServletResponse.SC_OK
),
@RestResponse(
description = "The event doesn't exist",
responseCode = HttpServletResponse.SC_NOT_FOUND
),
@RestResponse(
description = "The event cannot be accessed",
responseCode = HttpServletResponse.SC_UNAUTHORIZED
),
}
)
public Response getEventMetadata(@PathParam("eventId") final String eventId) {
try {
return Response.ok(service.getEventMetadata(eventId), MediaType.APPLICATION_JSON).build();
} catch (NotFoundException e) {
return Response.status(Status.NOT_FOUND).build();
} catch (UnauthorizedException e) {
return Response.status(Status.UNAUTHORIZED).build();
}
}
@DELETE
@Path("{eventId}")
@RestQuery(
name = "deleteevent",
description = "Deletes an event.",
returnDescription = "",
pathParameters = {
@RestParameter(name = "eventId", description = "The event id", isRequired = true, type = STRING)
},
responses = {
@RestResponse(
description = "The event has been deleted.",
responseCode = HttpServletResponse.SC_NO_CONTENT
),
@RestResponse(
description = "The specified event does not exist.",
responseCode = HttpServletResponse.SC_NOT_FOUND
)
}
)
public Response deleteEvent(@PathParam("eventId") final String id) {
service.delete(id);
return Response.noContent().build();
}
}