org.opencastproject.series.endpoint.SeriesRestService Maven / Gradle / Ivy
/**
* 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.series.endpoint;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_CREATED;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_IDENTIFIER;
import static org.opencastproject.util.doc.rest.RestParameter.Type.STRING;
import static org.opencastproject.util.doc.rest.RestParameter.Type.TEXT;
import org.opencastproject.mediapackage.EName;
import org.opencastproject.metadata.dublincore.DublinCore;
import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
import org.opencastproject.metadata.dublincore.DublinCoreCatalogService;
import org.opencastproject.metadata.dublincore.DublinCores;
import org.opencastproject.rest.RestConstants;
import org.opencastproject.security.api.AccessControlList;
import org.opencastproject.security.api.AccessControlParser;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.security.api.UnauthorizedException;
import org.opencastproject.series.api.Series;
import org.opencastproject.series.api.SeriesException;
import org.opencastproject.series.api.SeriesService;
import org.opencastproject.systems.OpencastConstants;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.RestUtil.R;
import org.opencastproject.util.UrlSupport;
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.entwinemedia.fn.Stream;
import com.entwinemedia.fn.data.Opt;
import com.entwinemedia.fn.data.json.JValue;
import com.entwinemedia.fn.data.json.Jsons;
import com.entwinemedia.fn.data.json.SimpleSerializer;
import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* REST endpoint for Series Service.
*
*/
@Path("/")
@RestService(
name = "seriesservice",
title = "Series Service",
abstractText = "This service creates, edits and retrieves and helps managing series.",
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",
"A status code 500 means a general failure has occurred which is not recoverable and was "
+ "not anticipated. In other words, there is a bug! You should file an error report "
+ "with your server logs from the time when the error occurred: "
+ "Opencast Issue Tracker"
}
)
@Component(
immediate = true,
service = SeriesRestService.class,
property = {
"service.description=Series REST Endpoint",
"opencast.service.type=org.opencastproject.series",
"opencast.service.path=/series"
}
)
public class SeriesRestService {
private static final String SERIES_ELEMENT_CONTENT_TYPE_PREFIX = "series/";
private static final Gson gson = new Gson();
/** Logging utility */
private static final Logger logger = LoggerFactory.getLogger(SeriesRestService.class);
/** Series Service */
private SeriesService seriesService;
/** Dublin Core Catalog service */
private DublinCoreCatalogService dcService;
/** The security service */
private SecurityService securityService;
/** Default server URL */
protected String serverUrl = "http://localhost:8080";
/** Service url */
protected String serviceUrl = null;
/** Default number of items on page */
private static final int DEFAULT_LIMIT = 20;
/** Suffix to mark descending ordering of results */
public static final String DESCENDING_SUFFIX = "_DESC";
private static final String SAMPLE_DUBLIN_CORE = "\n"
+ "\n\n"
+ " \n"
+ " Land and Vegetation: Key players on the Climate Scene\n"
+ " \n"
+ " "
+ " climate, land, vegetation\n"
+ " \n"
+ " \n"
+ " Introduction lecture from the Institute for\n"
+ " Atmospheric and Climate Science.\n"
+ " \n"
+ " \n"
+ " ETH Zurich, Switzerland\n"
+ " \n"
+ " \n"
+ " 10.0000/5819\n"
+ " \n"
+ " \n"
+ " 2007-12-05\n"
+ " \n"
+ " \n"
+ " video/x-dv\n"
+ " \n"
+ " ";
private static final String SAMPLE_ACCESS_CONTROL_LIST =
"\n"
+ "\n"
+ " \n"
+ " admin \n"
+ " delete \n"
+ " true \n"
+ " \n"
+ " ";
/**
* OSGi callback for setting series service.
*
* @param seriesService
*/
@Reference
public void setService(SeriesService seriesService) {
this.seriesService = seriesService;
}
/**
* OSGi callback for setting Dublin Core Catalog service.
*
* @param dcService
*/
@Reference
public void setDublinCoreService(DublinCoreCatalogService dcService) {
this.dcService = dcService;
}
/** OSGi callback for the security service */
@Reference
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
/**
* Activates REST service.
*
* @param cc
* ComponentContext
*/
@Activate
public void activate(ComponentContext cc) {
if (cc == null) {
this.serverUrl = "http://localhost:8080";
} else {
String ccServerUrl = cc.getBundleContext().getProperty(OpencastConstants.SERVER_URL_PROPERTY);
logger.debug("Configured server url is {}", ccServerUrl);
if (ccServerUrl == null) {
this.serverUrl = "http://localhost:8080";
} else {
this.serverUrl = ccServerUrl;
}
}
serviceUrl = (String) cc.getProperties().get(RestConstants.SERVICE_PATH_PROPERTY);
}
public String getSeriesXmlUrl(String seriesId) {
return UrlSupport.concat(serverUrl, serviceUrl, seriesId + ".xml");
}
public String getSeriesJsonUrl(String seriesId) {
return UrlSupport.concat(serverUrl, serviceUrl, seriesId + ".json");
}
@GET
@Produces(MediaType.TEXT_XML)
@Path("{seriesID:.+}.xml")
@RestQuery(
name = "getAsXml",
description = "Returns the series with the given identifier",
returnDescription = "Returns the series dublin core XML document",
pathParameters = {
@RestParameter(name = "seriesID", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(responseCode = SC_OK, description = "The series dublin core."),
@RestResponse(responseCode = SC_NOT_FOUND, description = "No series with this identifier was found."),
@RestResponse(responseCode = SC_FORBIDDEN, description = "You do not have permission to view this series."),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "You do not have permission to view this series. Maybe you need to authenticate."
)
}
)
public Response getSeriesXml(@PathParam("seriesID") String seriesID) {
logger.debug("Series Lookup: {}", seriesID);
try {
DublinCoreCatalog dc = this.seriesService.getSeries(seriesID);
return Response.ok(dc.toXmlString()).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
} catch (UnauthorizedException e) {
logger.warn("permission exception retrieving series");
// TODO this should be an 403 (Forbidden) if the user is logged in
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
} catch (Exception e) {
logger.error("Could not retrieve series: {}", e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{seriesID:.+}.json")
@RestQuery(
name = "getAsJson",
description = "Returns the series with the given identifier",
returnDescription = "Returns the series dublin core JSON document",
pathParameters = {
@RestParameter(name = "seriesID", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(responseCode = SC_OK, description = "The series dublin core."),
@RestResponse(responseCode = SC_NOT_FOUND, description = "No series with this identifier was found."),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "You do not have permission to view this series. Maybe you need to authenticate."
)
}
)
public Response getSeriesJSON(@PathParam("seriesID") String seriesID) {
logger.debug("Series Lookup: {}", seriesID);
try {
DublinCoreCatalog dc = this.seriesService.getSeries(seriesID);
return Response.ok(dc.toJson()).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
} catch (UnauthorizedException e) {
logger.warn("permission exception retrieving series");
// TODO this should be an 403 (Forbidden) if the user is logged in
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
} catch (Exception e) {
logger.error("Could not retrieve series: {}", e.getMessage());
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
}
@GET
@Produces(MediaType.TEXT_XML)
@Path("/{seriesID:.+}/acl.xml")
@RestQuery(
name = "getAclAsXml",
description = "Returns the access control list for the series with the given identifier",
returnDescription = "Returns the series ACL as XML",
pathParameters = {
@RestParameter(name = "seriesID", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(responseCode = SC_OK, description = "The access control list."),
@RestResponse(responseCode = SC_NOT_FOUND, description = "No series with this identifier was found.")
}
)
public Response getSeriesAccessControlListXml(@PathParam("seriesID") String seriesID) {
return getSeriesAccessControlList(seriesID);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{seriesID:.+}/acl.json")
@RestQuery(
name = "getAclAsJson",
description = "Returns the access control list for the series with the given identifier",
returnDescription = "Returns the series ACL as JSON",
pathParameters = {
@RestParameter(name = "seriesID", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(responseCode = SC_OK, description = "The access control list."),
@RestResponse(responseCode = SC_NOT_FOUND, description = "No series with this identifier was found.")
}
)
public Response getSeriesAccessControlListJson(@PathParam("seriesID") String seriesID) {
return getSeriesAccessControlList(seriesID);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/allInRangeAdministrative.json")
@RestQuery(
name = "allInRangeAdministrative",
description = "Internal API! Returns all series (included deleted ones!) in the given "
+ "range 'from' (inclusive) .. 'to' (exclusive). Returns at most 'limit' many series. "
+ "Can only be used as administrator!",
returnDescription = "Series in the range",
restParameters = {
@RestParameter(
name = "from",
isRequired = true,
description = "Start of date range (inclusive) in milliseconds "
+ "since 1970-01-01T00:00:00Z. Has to be >=0.",
type = Type.INTEGER
),
@RestParameter(
name = "to",
isRequired = false,
// TODO: this shows the default value as 0 despite us not setting this value!
description = "End of date range (exclusive) in milliseconds "
+ "since 1970-01-01T00:00:00Z. Has to be > 'from'.",
type = Type.INTEGER
),
@RestParameter(
name = "limit",
isRequired = true,
description = "Maximum number of series to be returned. Has to be >0.",
type = Type.INTEGER
),
},
responses = {
@RestResponse(responseCode = SC_OK, description = "All series in the range"),
@RestResponse(responseCode = SC_BAD_REQUEST, description = "if the given parameters are invalid"),
@RestResponse(responseCode = SC_UNAUTHORIZED, description = "If the user is not an administrator"),
}
)
public Response getAllInRangeAdministrative(
@FormParam("from") Long from,
@FormParam("to") Long to,
@FormParam("limit") Integer limit
) throws UnauthorizedException {
// Parameter error handling
if (from == null) {
return badRequestAllInRange("Required parameter 'from' not specified");
}
if (limit == null) {
return badRequestAllInRange("Required parameter 'limit' not specified");
}
if (from < 0) {
return badRequestAllInRange("Parameter 'from' < 0, but it has to be >= 0");
}
if (to != null && to <= from) {
return badRequestAllInRange("Parameter 'to' <= 'from', but that is not allowed");
}
if (limit <= 0) {
return badRequestAllInRange("Parameter 'limit' <= 0, but it has to be > 0");
}
try {
final List series = seriesService.getAllForAdministrativeRead(
new Date(from),
Optional.ofNullable(to).map(millis -> new Date(millis)),
limit);
return Response.ok(gson.toJson(series)).build();
} catch (SeriesException e) {
logger.error("Unexpected exception in getAllInRangeAdministrative", e);
return Response.status(INTERNAL_SERVER_ERROR)
.entity("internal server error")
.build();
}
}
/**
* Returns a {@code Response} object representing a BAD_REQUEST to `allInRangeAdministrative`
* with the given message as body. Also logs the message.
*/
private static Response badRequestAllInRange(String msg) {
logger.debug("Bad request to /series/allInRangeAdministrative: {}", msg);
return Response.status(BAD_REQUEST).entity(msg).build();
}
/**
* Retrieves ACL associated with series.
*
* @param seriesID
* series of which ACL should be retrieved
* @return
*/
private Response getSeriesAccessControlList(String seriesID) {
logger.debug("Series ACL lookup: {}", seriesID);
try {
AccessControlList acl = seriesService.getSeriesAccessControl(seriesID);
return Response.ok(acl).build();
} catch (NotFoundException e) {
return Response.status(NOT_FOUND).build();
} catch (SeriesException e) {
logger.error("Could not retrieve series ACL: {}", e.getMessage());
}
throw new WebApplicationException(INTERNAL_SERVER_ERROR);
}
private void addDcData(final DublinCoreCatalog dc, final String field, final String value) {
if (StringUtils.isNotBlank(value)) {
EName en = new EName(DublinCore.TERMS_NS_URI, field);
dc.add(en, value);
}
}
@POST
@Path("/")
@RestQuery(
name = "updateSeries",
description = "Updates a series",
returnDescription = "No content.",
restParameters = {
@RestParameter(
name = "series",
isRequired = false,
defaultValue = SAMPLE_DUBLIN_CORE,
description = "The series document. Will take precedence over metadata fields",
type = TEXT
),
@RestParameter(
name = "acl",
isRequired = false,
defaultValue = SAMPLE_ACCESS_CONTROL_LIST,
description = "The access control list for the series",
type = TEXT
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "accessRights",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "available",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "contributor",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "coverage",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "created",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "creator",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "date",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "description",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "extent",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "format",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "identifier",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "isPartOf",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "isReferencedBy",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "isReplacedBy",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "language",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "license",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "publisher",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "relation",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "replaces",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "rights",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "rightsHolder",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "source",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "spatial",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "subject",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "temporal",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "title",
type = RestParameter.Type.STRING
),
@RestParameter(
description = "Series metadata value",
isRequired = false,
name = "type",
type = RestParameter.Type.STRING
),
@RestParameter(
name = "override",
isRequired = false,
defaultValue = "false",
description = "If true the series ACL will take precedence over any existing episode ACL",
type = STRING
)
},
responses = {
@RestResponse(
responseCode = SC_BAD_REQUEST,
description = "The required form params were missing in the request."
),
@RestResponse(
responseCode = SC_NO_CONTENT,
description = "The access control list has been updated."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
),
@RestResponse(
responseCode = SC_CREATED,
description = "The access control list has been created."
)
}
)
public Response addOrUpdateSeries(
@FormParam("series") String series,
@FormParam("acl") String accessControl,
@FormParam("accessRights") String dcAccessRights,
@FormParam("available") String dcAvailable,
@FormParam("contributor") String dcContributor,
@FormParam("coverage") String dcCoverage,
@FormParam("created") String dcCreated,
@FormParam("creator") String dcCreator,
@FormParam("date") String dcDate,
@FormParam("description") String dcDescription,
@FormParam("extent") String dcExtent,
@FormParam("format") String dcFormat,
@FormParam("identifier") String dcIdentifier,
@FormParam("isPartOf") String dcIsPartOf,
@FormParam("isReferencedBy") String dcIsReferencedBy,
@FormParam("isReplacedBy") String dcIsReplacedBy,
@FormParam("language") String dcLanguage,
@FormParam("license") String dcLicense,
@FormParam("publisher") String dcPublisher,
@FormParam("relation") String dcRelation,
@FormParam("replaces") String dcReplaces,
@FormParam("rights") String dcRights,
@FormParam("rightsHolder") String dcRightsHolder,
@FormParam("source") String dcSource,
@FormParam("spatial") String dcSpatial,
@FormParam("subject") String dcSubject,
@FormParam("temporal") String dcTemporal,
@FormParam("title") String dcTitle,
@FormParam("type") String dcType,
@DefaultValue("false") @FormParam("override") boolean override
) throws UnauthorizedException {
DublinCoreCatalog dc;
if (StringUtils.isNotBlank(series)) {
try {
dc = this.dcService.load(new ByteArrayInputStream(series.getBytes(StandardCharsets.UTF_8)));
} catch (UnsupportedEncodingException e1) {
logger.error("Could not deserialize dublin core catalog", e1);
throw new WebApplicationException(INTERNAL_SERVER_ERROR);
} catch (IOException e1) {
logger.warn("Could not deserialize dublin core catalog", e1);
return Response.status(BAD_REQUEST).build();
}
} else if (StringUtils.isNotBlank(dcTitle)) {
dc = DublinCores.mkOpencastSeries().getCatalog();
addDcData(dc, "accessRights", dcAccessRights);
addDcData(dc, "available", dcAvailable);
addDcData(dc, "contributor", dcContributor);
addDcData(dc, "coverage", dcCoverage);
addDcData(dc, "created", dcCreated);
addDcData(dc, "creator", dcCreator);
addDcData(dc, "date", dcDate);
addDcData(dc, "description", dcDescription);
addDcData(dc, "extent", dcExtent);
addDcData(dc, "format", dcFormat);
addDcData(dc, "identifier", dcIdentifier);
addDcData(dc, "isPartOf", dcIsPartOf);
addDcData(dc, "isReferencedBy", dcIsReferencedBy);
addDcData(dc, "isReplacedBy", dcIsReplacedBy);
addDcData(dc, "language", dcLanguage);
addDcData(dc, "license", dcLicense);
addDcData(dc, "publisher", dcPublisher);
addDcData(dc, "relation", dcRelation);
addDcData(dc, "replaces", dcReplaces);
addDcData(dc, "rights", dcRights);
addDcData(dc, "rightsHolder", dcRightsHolder);
addDcData(dc, "source", dcSource);
addDcData(dc, "spatial", dcSpatial);
addDcData(dc, "subject", dcSubject);
addDcData(dc, "temporal", dcTemporal);
addDcData(dc, "title", dcTitle);
addDcData(dc, "type", dcType);
} else {
return Response.status(BAD_REQUEST).entity("Required series metadata not provided").build();
}
AccessControlList acl = null;
if (StringUtils.isNotBlank(accessControl)) {
try {
acl = AccessControlParser.parseAcl(accessControl);
} catch (Exception e) {
logger.debug("Could not parse ACL", e);
return Response.status(BAD_REQUEST).entity("Could not parse ACL").build();
}
}
try {
DublinCoreCatalog newSeries = seriesService.updateSeries(dc);
if (acl != null) {
seriesService.updateAccessControl(dc.getFirst(PROPERTY_IDENTIFIER), acl, override);
}
if (newSeries == null) {
logger.debug("Updated series {} ", dc.getFirst(PROPERTY_IDENTIFIER));
return Response.status(NO_CONTENT).build();
}
String id = newSeries.getFirst(PROPERTY_IDENTIFIER);
logger.debug("Created series {} ", id);
return Response.status(CREATED)
.header("Location", getSeriesXmlUrl(id))
.header("Location", getSeriesJsonUrl(id))
.entity(newSeries.toXmlString())
.build();
} catch (UnauthorizedException e) {
throw e;
} catch (Exception e) {
logger.error("Could not add/update series", e);
}
return Response.serverError().build();
}
@POST
@Path("/{seriesID:.+}/accesscontrol")
@RestQuery(
name = "updateAcl",
description = "Updates the access control list for a series",
returnDescription = "No content.",
restParameters = {
@RestParameter(
name = "acl",
isRequired = true,
defaultValue = SAMPLE_ACCESS_CONTROL_LIST,
description = "The access control list for the series",
type = TEXT
),
@RestParameter(
name = "override",
isRequired = false,
defaultValue = "false",
description = "If true the series ACL will take precedence over any existing episode ACL",
type = STRING
)
},
pathParameters = {
@RestParameter(name = "seriesID", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(
responseCode = SC_NOT_FOUND,
description = "No series with this identifier was found."
),
@RestResponse(
responseCode = SC_NO_CONTENT,
description = "The access control list has been updated."
),
@RestResponse(
responseCode = SC_CREATED,
description = "The access control list has been created."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
),
@RestResponse(
responseCode = SC_BAD_REQUEST,
description = "The required path or form params were missing in the request."
)
}
)
public Response updateAccessControl(
@PathParam("seriesID") String seriesID,
@FormParam("acl") String accessControl,
@DefaultValue("false") @FormParam("override") boolean override
) throws UnauthorizedException {
if (accessControl == null) {
logger.warn("Access control parameter is null.");
return Response.status(BAD_REQUEST).build();
}
AccessControlList acl;
try {
acl = AccessControlParser.parseAcl(accessControl);
} catch (Exception e) {
logger.warn("Could not parse ACL: {}", e.getMessage());
return Response.status(BAD_REQUEST).build();
}
try {
boolean updated = seriesService.updateAccessControl(seriesID, acl, override);
if (updated) {
return Response.status(NO_CONTENT).build();
}
return Response.status(CREATED).build();
} catch (NotFoundException e) {
return Response.status(NOT_FOUND).build();
} catch (SeriesException e) {
logger.warn("Could not update ACL for {}: {}", seriesID, e.getMessage());
}
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
@DELETE
@Path("/{seriesID:.+}")
@RestQuery(
name = "delete",
description = "Delete a series",
returnDescription = "No content.",
pathParameters = {
@RestParameter(name = "seriesID", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(
responseCode = SC_NOT_FOUND,
description = "No series with this identifier was found."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
),
@RestResponse(
responseCode = SC_NO_CONTENT,
description = "The series was deleted."
)
}
)
public Response deleteSeries(@PathParam("seriesID") String seriesID) throws UnauthorizedException {
try {
this.seriesService.deleteSeries(seriesID);
return Response.ok().build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
} catch (SeriesException se) {
logger.warn("Could not delete series {}: {}", seriesID, se.getMessage());
}
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/count")
@RestQuery(
name = "count",
description = "Returns the number of series",
returnDescription = "Returns the number of series",
responses = {
@RestResponse(responseCode = SC_OK, description = "The number of series")
}
)
public Response getCount() throws UnauthorizedException {
try {
int count = seriesService.getSeriesCount();
return Response.ok(count).build();
} catch (SeriesException se) {
logger.warn("Could not count series: {}", se.getMessage());
throw new WebApplicationException(se);
}
}
@SuppressWarnings("unchecked")
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}/properties.json")
@RestQuery(
name = "getSeriesProperties",
description = "Returns the series properties",
returnDescription = "Returns the series properties as JSON",
pathParameters = {
@RestParameter(name = "id", description = "ID of series", isRequired = true, type = Type.STRING)
},
responses = {
@RestResponse(
responseCode = SC_OK,
description = "The access control list."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
)
}
)
public Response getSeriesPropertiesAsJson(@PathParam("id") String seriesId)
throws UnauthorizedException, NotFoundException {
if (StringUtils.isBlank(seriesId)) {
logger.warn("Series id parameter is blank '{}'.", seriesId);
return Response.status(BAD_REQUEST).build();
}
try {
Map properties = seriesService.getSeriesProperties(seriesId);
JSONArray jsonProperties = new JSONArray();
for (String name : properties.keySet()) {
JSONObject property = new JSONObject();
property.put(name, properties.get(name));
jsonProperties.add(property);
}
return Response.ok(jsonProperties.toString()).build();
} catch (UnauthorizedException e) {
throw e;
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.warn("Could not perform search query: {}", e.getMessage());
}
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{seriesId}/property/{propertyName}.json")
@RestQuery(
name = "getSeriesProperty",
description = "Returns a series property value",
returnDescription = "Returns the series property value",
pathParameters = {
@RestParameter(
name = "seriesId",
description = "ID of series",
isRequired = true,
type = Type.STRING
),
@RestParameter(
name = "propertyName",
description = "Name of series property",
isRequired = true,
type = Type.STRING
)
},
responses = {
@RestResponse(
responseCode = SC_OK,
description = "The access control list."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
)
}
)
public Response getSeriesProperty(@PathParam("seriesId") String seriesId,
@PathParam("propertyName") String propertyName) throws UnauthorizedException, NotFoundException {
if (StringUtils.isBlank(seriesId)) {
logger.warn("Series id parameter is blank '{}'.", seriesId);
return Response.status(BAD_REQUEST).build();
}
if (StringUtils.isBlank(propertyName)) {
logger.warn("Series property name parameter is blank '{}'.", propertyName);
return Response.status(BAD_REQUEST).build();
}
try {
String propertyValue = seriesService.getSeriesProperty(seriesId, propertyName);
return Response.ok(propertyValue).build();
} catch (UnauthorizedException e) {
throw e;
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.warn("Could not perform search query", e);
}
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
@POST
@Path("/{seriesId}/property")
@RestQuery(
name = "updateSeriesProperty",
description = "Updates a series property",
returnDescription = "No content.",
restParameters = {
@RestParameter(name = "name", isRequired = true, description = "The property's name", type = TEXT),
@RestParameter(name = "value", isRequired = true, description = "The property's value", type = TEXT)
},
pathParameters = {
@RestParameter(name = "seriesId", isRequired = true, description = "The series identifier", type = STRING)
},
responses = {
@RestResponse(
responseCode = SC_NOT_FOUND,
description = "No series with this identifier was found."
),
@RestResponse(
responseCode = SC_NO_CONTENT,
description = "The access control list has been updated."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
),
@RestResponse(
responseCode = SC_BAD_REQUEST,
description = "The required path or form params were missing in the request."
)
}
)
public Response updateSeriesProperty(
@PathParam("seriesId") String seriesId,
@FormParam("name") String name,
@FormParam("value") String value
) throws UnauthorizedException {
if (StringUtils.isBlank(seriesId)) {
logger.warn("Series id parameter is blank '{}'.", seriesId);
return Response.status(BAD_REQUEST).build();
}
if (StringUtils.isBlank(name)) {
logger.warn("Name parameter is blank '{}'.", name);
return Response.status(BAD_REQUEST).build();
}
if (StringUtils.isBlank(value)) {
logger.warn("Series id parameter is blank '{}'.", value);
return Response.status(BAD_REQUEST).build();
}
try {
seriesService.updateSeriesProperty(seriesId, name, value);
return Response.status(NO_CONTENT).build();
} catch (NotFoundException e) {
return Response.status(NOT_FOUND).build();
} catch (SeriesException e) {
logger.warn("Could not update series property for series {} property {}:{} :", seriesId, name, value, e);
}
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
@DELETE
@Path("{seriesId}/property/{propertyName}")
@RestQuery(
name = "deleteSeriesProperty",
description = "Deletes a series property",
returnDescription = "No Content",
pathParameters = {
@RestParameter(
name = "seriesId",
description = "ID of series",
isRequired = true,
type = Type.STRING
),
@RestParameter(
name = "propertyName",
description = "Name of series property",
isRequired = true,
type = Type.STRING
)
},
responses = {
@RestResponse(
responseCode = SC_NO_CONTENT,
description = "The series property has been deleted."
),
@RestResponse(
responseCode = SC_NOT_FOUND,
description = "The series or property has not been found."
),
@RestResponse(
responseCode = SC_UNAUTHORIZED,
description = "If the current user is not authorized to perform this action"
)
}
)
public Response deleteSeriesProperty(
@PathParam("seriesId") String seriesId,
@PathParam("propertyName") String propertyName
) throws UnauthorizedException, NotFoundException {
if (StringUtils.isBlank(seriesId)) {
logger.warn("Series id parameter is blank '{}'.", seriesId);
return Response.status(BAD_REQUEST).build();
}
if (StringUtils.isBlank(propertyName)) {
logger.warn("Series property name parameter is blank '{}'.", propertyName);
return Response.status(BAD_REQUEST).build();
}
try {
seriesService.deleteSeriesProperty(seriesId, propertyName);
return Response.status(NO_CONTENT).build();
} catch (UnauthorizedException e) {
throw e;
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.warn("Could not delete series '{}' property '{}' query:", seriesId, propertyName, e);
}
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
@GET
@Path("{seriesId}/elements.json")
@Produces(MediaType.APPLICATION_JSON)
@RestQuery(
name = "getSeriesElements",
description = "Returns all the element types of a series",
returnDescription = "Returns a JSON array with all the types of elements of the given series.",
pathParameters = {
@RestParameter(name = "seriesId", description = "The series identifier", type = STRING, isRequired = true)
},
responses = {
@RestResponse(responseCode = SC_OK, description = "Series found"),
@RestResponse(responseCode = SC_NOT_FOUND, description = "Series not found"),
@RestResponse(responseCode = SC_INTERNAL_SERVER_ERROR, description = "Error while processing the request")
}
)
public Response getSeriesElements(@PathParam("seriesId") String seriesId) {
try {
Opt
© 2015 - 2025 Weber Informatics LLC | Privacy Policy