1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.editor.api;
23
24 import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
25 import static javax.servlet.http.HttpServletResponse.SC_CREATED;
26 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
27 import static javax.servlet.http.HttpServletResponse.SC_OK;
28
29 import org.opencastproject.security.api.UnauthorizedException;
30 import org.opencastproject.util.RestUtil;
31 import org.opencastproject.util.doc.rest.RestParameter;
32 import org.opencastproject.util.doc.rest.RestQuery;
33 import org.opencastproject.util.doc.rest.RestResponse;
34
35 import org.apache.commons.io.IOUtils;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.net.URI;
42
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
45 import javax.ws.rs.Consumes;
46 import javax.ws.rs.DELETE;
47 import javax.ws.rs.FormParam;
48 import javax.ws.rs.GET;
49 import javax.ws.rs.POST;
50 import javax.ws.rs.Path;
51 import javax.ws.rs.PathParam;
52 import javax.ws.rs.Produces;
53 import javax.ws.rs.core.Context;
54 import javax.ws.rs.core.MediaType;
55 import javax.ws.rs.core.Response;
56
57
58
59
60 public abstract class EditorRestEndpointBase {
61
62 private static final Logger logger = LoggerFactory.getLogger(EditorRestEndpointBase.class);
63
64
65 protected EditorService editorService;
66
67 public abstract void setEditorService(EditorService service);
68
69 @GET
70 @Path("{mediaPackageId}/edit.json")
71 @Produces(MediaType.APPLICATION_JSON)
72 @RestQuery(name = "getEditorData",
73 description = "Returns all the information required to get the editor tool started",
74 returnDescription = "JSON object",
75 pathParameters = { @RestParameter(name = "mediaPackageId", description = "The id of the media package",
76 isRequired = true, type = RestParameter.Type.STRING) }, responses = {
77 @RestResponse(description = "Media package found", responseCode = SC_OK),
78 @RestResponse(description = "Media package not found", responseCode = SC_NOT_FOUND) })
79 public Response getEditorData(@PathParam("mediaPackageId") final String mediaPackageId) {
80 try {
81 EditingData response = editorService.getEditData(mediaPackageId);
82 if (response != null) {
83 return Response.ok(response.toString(), MediaType.APPLICATION_JSON_TYPE).build();
84 } else {
85 logger.error("EditorService returned empty response");
86 return RestUtil.R.serverError();
87 }
88 } catch (EditorServiceException e) {
89 return checkErrorState(mediaPackageId, e);
90 } catch (UnauthorizedException e) {
91 return Response.status(Response.Status.FORBIDDEN).entity("No write access to this event.").build();
92 }
93 }
94
95 @POST
96 @Path("{mediaPackageId}/lock")
97 @RestQuery(name = "lockMediapackage",
98 description = "Creates and updates the lock for a mediapackage in the editor. "
99 + "Requests will create/refreshen a lock at /editor/{mediapackageId}/lock{uuid} "
100 + "(see Location header in response) that will expire after the configured period. "
101 + "Subsequent calls must have the same uuid, which will then freshen the lock.",
102 returnDescription = "The lock is returned in the Location header.",
103 pathParameters = {
104 @RestParameter(name = "mediaPackageId", description = "The id of the media package", isRequired = true,
105 type = RestParameter.Type.STRING)
106 },
107 restParameters = {
108 @RestParameter(name = "user", isRequired = true,
109 description = "The user requesting to lock this mediapackage",
110 type = RestParameter.Type.STRING, defaultValue = "admin"),
111 @RestParameter(name = "uuid", isRequired = true,
112 description = "The unique identitier of the lock",
113 type = RestParameter.Type.STRING)
114 },
115 responses = {
116 @RestResponse(description = "Lock obtained", responseCode = SC_CREATED),
117 @RestResponse(description = "Lock not obtained", responseCode = SC_CONFLICT),
118 @RestResponse(description = "Mediapackage not found", responseCode = SC_NOT_FOUND)
119 })
120 public Response lockMediapackage(@PathParam("mediaPackageId") final String mediaPackageId,
121 @FormParam("user") final String user, @FormParam("uuid") final String uuid,
122 @Context HttpServletRequest request) {
123 try {
124 LockData lockData = new LockData(uuid, user);
125 editorService.lockMediaPackage(mediaPackageId, lockData);
126 return RestUtil.R.created(new URI(request.getRequestURI() + "/" + lockData.getUUID()));
127 } catch (EditorServiceException e) {
128 return checkErrorState(mediaPackageId, e);
129 } catch (Exception e) {
130 logger.debug("Unable to create lock", e);
131 return RestUtil.R.badRequest();
132 }
133 }
134
135 @DELETE
136 @Path("{mediaPackageId}/lock/{uuid}")
137 @RestQuery(name = "unlockMediapackage",
138 description = "Releases the lock for a mediapackage in the editor",
139 returnDescription = "",
140 pathParameters = {
141 @RestParameter(name = "mediaPackageId", description = "The id of the media package", isRequired = true,
142 type = RestParameter.Type.STRING),
143 @RestParameter(name = "uuid", description = "Identifier of editor session", isRequired = true,
144 type = RestParameter.Type.STRING)
145 },
146 responses = {
147 @RestResponse(description = "Lock deleted", responseCode = SC_OK),
148 @RestResponse(description = "Lock not obtained", responseCode = SC_CONFLICT),
149 @RestResponse(description = "Lock not found", responseCode = SC_NOT_FOUND)
150 })
151
152 public Response unlockMediapackage(@PathParam("mediaPackageId") final String mediaPackageId,
153 @PathParam("uuid") final String uuid) {
154 try {
155 editorService.unlockMediaPackage(mediaPackageId, new LockData(uuid, ""));
156 return RestUtil.R.ok();
157 } catch (EditorServiceException e) {
158 return checkErrorState(mediaPackageId, e);
159 }
160 }
161
162 @POST
163 @Path("{mediaPackageId}/edit.json")
164 @Consumes(MediaType.APPLICATION_JSON)
165 @RestQuery(name = "editVideo", description = "Takes editing information from the client side and processes it",
166 returnDescription = "",
167 pathParameters = {
168 @RestParameter(name = "mediaPackageId", description = "The id of the media package", isRequired = true,
169 type = RestParameter.Type.STRING) },
170 responses = {
171 @RestResponse(description = "Editing information saved and processed", responseCode = SC_OK),
172 @RestResponse(description = "Media package not found", responseCode = SC_NOT_FOUND),
173 @RestResponse(description = "The editing information cannot be parsed",
174 responseCode = HttpServletResponse.SC_BAD_REQUEST) })
175 public Response editVideo(@PathParam("mediaPackageId") final String mediaPackageId,
176 @Context HttpServletRequest request) {
177 String details = null;
178 try {
179 details = readInputStream(request);
180 logger.debug("Editor POST-Request received: {}", details);
181 EditingData editingInfo = EditingData.parse(details);
182 editorService.setEditData(mediaPackageId, editingInfo);
183 } catch (EditorServiceException e) {
184 return checkErrorState(mediaPackageId, e);
185 } catch (Exception e) {
186 logger.debug("Unable to parse editing information ({})", details, e);
187 return RestUtil.R.badRequest("Unable to parse details");
188 }
189
190 return RestUtil.R.ok();
191 }
192
193 protected String readInputStream(HttpServletRequest request) {
194 String details;
195 try (InputStream is = request.getInputStream()) {
196 details = IOUtils.toString(is, request.getCharacterEncoding());
197 } catch (IOException e) {
198 logger.error("Error reading request body:", e);
199 return null;
200 }
201 return details;
202 }
203
204 protected Response checkErrorState(final String eventId, EditorServiceException e) {
205 switch (e.getErrorStatus()) {
206 case MEDIAPACKAGE_NOT_FOUND:
207 return RestUtil.R.notFound(String.format("Event '%s' not Found", eventId), MediaType.TEXT_PLAIN_TYPE);
208 case MEDIAPACKAGE_LOCKED:
209 return RestUtil.R.conflict(String.format("Event '%s' is %s", eventId, e.getMessage()));
210 case WORKFLOW_ACTIVE:
211 return RestUtil.R.locked();
212 case NOT_AUTHORIZED:
213 return RestUtil.R.unauthorized(String.format("Unauthorized for event '%s'", eventId));
214 case WORKFLOW_NOT_FOUND:
215 case METADATA_UPDATE_FAIL:
216 case NO_INTERNAL_PUBLICATION:
217 return RestUtil.R.badRequest(e.getMessage());
218 case UNABLE_TO_CREATE_CATALOG:
219 case WORKFLOW_ERROR:
220 case UNKNOWN:
221 default:
222 return RestUtil.R.serverError();
223 }
224 }
225
226 @GET
227 @Path("{mediaPackageId}/metadata.json")
228 @Produces(MediaType.APPLICATION_JSON)
229 @RestQuery(name = "getMetadata",
230 description = "Returns all the data related to the metadata tab in the event details modal as JSON",
231 returnDescription = "All the data related to the event metadata tab as JSON",
232 pathParameters = {
233 @RestParameter(name = "mediaPackageId", description = "The event id", isRequired = true,
234 type = RestParameter.Type.STRING) },
235 responses = {
236 @RestResponse(description = "Returns all the data related to the event metadata tab as JSON",
237 responseCode = HttpServletResponse.SC_OK),
238 @RestResponse(description = "No event with this identifier was found.",
239 responseCode = HttpServletResponse.SC_NOT_FOUND) })
240 public Response getEventMetadata(@PathParam("mediaPackageId") String eventId) {
241 try {
242 String response = editorService.getMetadata(eventId);
243 if (response != null) {
244 return Response.ok(response, MediaType.APPLICATION_JSON_TYPE).build();
245 } else {
246 logger.error("EditorService returned empty response");
247 return RestUtil.R.serverError();
248 }
249 } catch (EditorServiceException e) {
250 return checkErrorState(eventId, e);
251 }
252 }
253 }