Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MH-13018 re-add recordings json to 5x (includes MH-12828 re-add conflicts.json) #367

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -100,6 +100,9 @@
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -1034,23 +1037,27 @@ public Response updateEvent(@PathParam("id") String eventID, @FormParam("start")
}

@GET
@Produces(MediaType.TEXT_XML)
@Path("recordings.xml")
@RestQuery(name = "recordingsasxml", description = "Searches recordings and returns result as XML", returnDescription = "XML formated results", restParameters = {
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("recordings.{type:xml|json}")
@RestQuery(name = "recordingsaslist", description = "Searches recordings and returns result as XML or JSON", returnDescription = "XML or JSON formated results",
pathParameters = {
@RestParameter(name = "type", isRequired = true, description = "The media type of the response [xml|json]", type = Type.STRING) },
restParameters = {
@RestParameter(name = "agent", description = "Search by device", isRequired = false, type = Type.STRING),
@RestParameter(name = "startsfrom", description = "Search by when does event start", isRequired = false, type = Type.INTEGER),
@RestParameter(name = "startsto", description = "Search by when does event start", isRequired = false, type = Type.INTEGER),
@RestParameter(name = "endsfrom", description = "Search by when does event finish", isRequired = false, type = Type.INTEGER),
@RestParameter(name = "endsto", description = "Search by when does event finish", isRequired = false, type = Type.INTEGER) }, reponses = {
@RestResponse(responseCode = HttpServletResponse.SC_OK, description = "Search completed, results returned in body") })
public Response getEventsAsXml(@QueryParam("agent") String device, @QueryParam("startsfrom") Long startsFromTime,
@RestParameter(name = "endsto", description = "Search by when does event finish", isRequired = false, type = Type.INTEGER) },
reponses = {
@RestResponse(responseCode = HttpServletResponse.SC_OK, description = "Search completed, results returned in body") })
public Response getEventsAsList(@PathParam("type") final String type, @QueryParam("agent") String device,
@QueryParam("startsfrom") Long startsFromTime,
@QueryParam("startsto") Long startsToTime, @QueryParam("endsfrom") Long endsFromTime,
@QueryParam("endsto") Long endsToTime) throws UnauthorizedException {
Date startsfrom = null;
Date startsTo = null;
Date endsFrom = null;
Date endsTo = null;

if (startsFromTime != null)
startsfrom = new DateTime(startsFromTime).toDateTime(DateTimeZone.UTC).toDate();
if (startsToTime != null)
Expand All @@ -1063,7 +1070,11 @@ public Response getEventsAsXml(@QueryParam("agent") String device, @QueryParam("
try {
List<MediaPackage> events = service.search(Opt.nul(StringUtils.trimToNull(device)), Opt.nul(startsfrom),
Opt.nul(startsTo), Opt.nul(endsFrom), Opt.nul(endsTo));
return Response.ok(MediaPackageParser.getArrayAsXml(events)).build();
if ("json".equalsIgnoreCase(type)) {
return Response.ok(getEventListAsJsonString(events)).build();
} else {
return Response.ok(MediaPackageParser.getArrayAsXml(events)).build();
}
} catch (UnauthorizedException e) {
throw e;
} catch (Exception e) {
Expand All @@ -1073,68 +1084,44 @@ public Response getEventsAsXml(@QueryParam("agent") String device, @QueryParam("
}

@GET
@Produces(MediaType.TEXT_XML)
@Path("conflicts.xml")
@RestQuery(name = "conflictingrecordingsasxml", description = "Searches for conflicting recordings based on parameters", returnDescription = "Returns NO CONTENT if no recordings are in conflict within specified period or list of conflicting recordings in XML", restParameters = {
@RestParameter(name = "agent", description = "Device identifier for which conflicts will be searched", isRequired = true, type = Type.STRING),
@RestParameter(name = "start", description = "Start time of conflicting period, in milliseconds", isRequired = true, type = Type.INTEGER),
@RestParameter(name = "end", description = "End time of conflicting period, in milliseconds", isRequired = true, type = Type.INTEGER),
@RestParameter(name = "rrule", description = "Rule for recurrent conflicting, specified as: \"FREQ=WEEKLY;BYDAY=day(s);BYHOUR=hour;BYMINUTE=minute\". FREQ is required. BYDAY may include one or more (separated by commas) of the following: SU,MO,TU,WE,TH,FR,SA.", isRequired = false, type = Type.STRING),
@RestParameter(name = "duration", description = "If recurrence rule is specified duration of each conflicting period, in milliseconds", isRequired = false, type = Type.INTEGER),
@RestParameter(name = "timezone", description = "The timezone of the capture device", isRequired = false, type = Type.STRING) }, reponses = {
@RestResponse(responseCode = HttpServletResponse.SC_NO_CONTENT, description = "No conflicting events found"),
@RestResponse(responseCode = HttpServletResponse.SC_OK, description = "Found conflicting events, returned in body of response"),
@RestResponse(responseCode = HttpServletResponse.SC_BAD_REQUEST, description = "Missing or invalid parameters") })
public Response getConflictingEventsXml(@QueryParam("agent") String device, @QueryParam("rrule") String rrule,
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("conflicts.{type:xml|json}")
@RestQuery(name = "conflictingrecordings", description = "Searches for conflicting recordings based on parameters and returns result as XML or JSON", returnDescription = "Returns NO CONTENT if no recordings are in conflict within specified period or list of conflicting recordings in XML or JSON",
pathParameters = {
@RestParameter(name = "type", isRequired = true, description = "The media type of the response [xml|json]", type = Type.STRING) },
restParameters = {
@RestParameter(name = "agent", description = "Device identifier for which conflicts will be searched", isRequired = true, type = Type.STRING),
@RestParameter(name = "start", description = "Start time of conflicting period, in milliseconds", isRequired = true, type = Type.INTEGER),
@RestParameter(name = "end", description = "End time of conflicting period, in milliseconds", isRequired = true, type = Type.INTEGER),
@RestParameter(name = "rrule", description = "Rule for recurrent conflicting, specified as: \"FREQ=WEEKLY;BYDAY=day(s);BYHOUR=hour;BYMINUTE=minute\". FREQ is required. BYDAY may include one or more (separated by commas) of the following: SU,MO,TU,WE,TH,FR,SA.", isRequired = false, type = Type.STRING),
@RestParameter(name = "duration", description = "If recurrence rule is specified duration of each conflicting period, in milliseconds", isRequired = false, type = Type.INTEGER),
@RestParameter(name = "timezone", description = "The timezone of the capture device", isRequired = false, type = Type.STRING) }, reponses = {
@RestResponse(responseCode = HttpServletResponse.SC_NO_CONTENT, description = "No conflicting events found"),
@RestResponse(responseCode = HttpServletResponse.SC_OK, description = "Found conflicting events, returned in body of response"),
@RestResponse(responseCode = HttpServletResponse.SC_BAD_REQUEST, description = "Missing or invalid parameters"),
@RestResponse(responseCode = HttpServletResponse.SC_UNAUTHORIZED, description = "Not authorized to make this request"),
@RestResponse(responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR, description = "A detailed stack track of the internal issue.")})
public Response getConflicts(@PathParam("type") final String type, @QueryParam("agent") String device, @QueryParam("rrule") String rrule,
@QueryParam("start") Long startDate, @QueryParam("end") Long endDate, @QueryParam("duration") Long duration,
@QueryParam("timezone") String timezone) throws UnauthorizedException {
if (StringUtils.isBlank(device) || startDate == null || endDate == null) {
logger.info("Either agent, start date or end date were not specified");
return Response.status(Status.BAD_REQUEST).build();
}

RRule rule = null;
if (StringUtils.isNotBlank(rrule)) {
if (duration == null || StringUtils.isBlank(timezone)) {
logger.info("Either duration or timezone were not specified");
return Response.status(Status.BAD_REQUEST).build();
}

try {
rule = new RRule(rrule);
rule.validate();
} catch (Exception e) {
logger.info("Unable to parse rrule {}: {}", rrule, getMessage(e));
return Response.status(Status.BAD_REQUEST).build();
}

if (!Arrays.asList(TimeZone.getAvailableIDs()).contains(timezone)) {
logger.info("Unable to parse timezone: {}", timezone);
return Response.status(Status.BAD_REQUEST).build();
}
}

Date start = new DateTime(startDate).toDateTime(DateTimeZone.UTC).toDate();

Date end = new DateTime(endDate).toDateTime(DateTimeZone.UTC).toDate();

try {
List<MediaPackage> events;
if (StringUtils.isNotBlank(rrule)) {
events = service.findConflictingEvents(device, rule, start, end, duration, TimeZone.getTimeZone(timezone));
} else {
events = service.findConflictingEvents(device, start, end);
}
List<MediaPackage> events = getConflictingEvents(device, rrule, startDate, endDate, duration, timezone);
if (!events.isEmpty()) {
return Response.ok(MediaPackageParser.getArrayAsXml(events)).build();
if ("json".equalsIgnoreCase(type)) {
return Response.ok(getEventListAsJsonString(events)).build();
} else {
return Response.ok(MediaPackageParser.getArrayAsXml(events)).build();
}
} else {
return Response.noContent().build();
}
} catch (IllegalArgumentException e) {
return Response.status(Status.BAD_REQUEST).build();
} catch (UnauthorizedException e) {
throw e;
} catch (Exception e) {
logger.error("Unable to find conflicting events for {}, {}, {}, {}, {}: {}",
device, rrule, start, end, duration, getStackTrace(e));
device, rrule, startDate, endDate, duration, getStackTrace(e));
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
}
Expand Down Expand Up @@ -1743,6 +1730,50 @@ public Response prolongCapture(@PathParam("agent") String agentId) throws NotFou
}
}

private List<MediaPackage> getConflictingEvents(String device, String rrule,
Long startDate, Long endDate, Long duration, String timezone)
throws IllegalArgumentException, UnauthorizedException, SchedulerException {

List<MediaPackage> events = null;

if (StringUtils.isBlank(device) || startDate == null || endDate == null) {
logger.info("Either agent, start date or end date were not specified");
throw new IllegalArgumentException();
}

RRule rule = null;
if (StringUtils.isNotBlank(rrule)) {
if (duration == null || StringUtils.isBlank(timezone)) {
logger.info("Either duration or timezone were not specified");
throw new IllegalArgumentException();
}

try {
rule = new RRule(rrule);
rule.validate();
} catch (Exception e) {
logger.info("Unable to parse rrule {}: {}", rrule, getMessage(e));
throw new IllegalArgumentException();
}

if (!Arrays.asList(TimeZone.getAvailableIDs()).contains(timezone)) {
logger.info("Unable to parse timezone: {}", timezone);
throw new IllegalArgumentException();
}
}

Date start = new DateTime(startDate).toDateTime(DateTimeZone.UTC).toDate();

Date end = new DateTime(endDate).toDateTime(DateTimeZone.UTC).toDate();

if (StringUtils.isNotBlank(rrule)) {
events = service.findConflictingEvents(device, rule, start, end, duration, TimeZone.getTimeZone(timezone));
} else {
events = service.findConflictingEvents(device, start, end);
}
return events;
}

private MediaPackage addCatalog(Workspace workspace, InputStream in, String fileName,
MediaPackageElementFlavor flavor, MediaPackage mediaPackage) throws IOException {
Catalog[] catalogs = mediaPackage.getCatalogs(flavor);
Expand Down Expand Up @@ -1793,4 +1824,32 @@ private Properties parseProperties(String serializedProperties) throws IOExcepti
caProperties.load(new StringReader(serializedProperties));
return caProperties;
}

/**
* Serializes mediapackage schedule metadata into JSON array string.
*
* @return serialized array as json array string
* @throws SchedulerException
* if parsing list into JSON format fails
*/
public String getEventListAsJsonString(List<MediaPackage> mpList) throws SchedulerException {
JSONParser parser = new JSONParser();
JSONObject jsonObj = new JSONObject();
JSONArray jsonArray = new JSONArray();
for (MediaPackage mp: mpList) {
JSONObject mpJson;
try {
mpJson = (JSONObject) parser.parse(MediaPackageParser.getAsJSON(mp));
mpJson = (JSONObject) mpJson.get("mediapackage");
jsonArray.add(mpJson);
} catch (org.json.simple.parser.ParseException e) {
logger.warn("Unexpected JSON parse exception for getAsJSON on mp {}", mp.getIdentifier().compact(), e);
throw new SchedulerException(e);
}
}
jsonObj.put("totalCount", String.valueOf(mpList.size()));
jsonObj.put("events", jsonArray);
return jsonObj.toJSONString();
}
}

Expand Up @@ -2298,6 +2298,16 @@ public void testRepopulateIndexMultitenant() throws Exception {
assertEquals(orgList.size(), dublincoreCatalogs.size());
}

public void testGetJsonMpSchedulerRestEndpoint() throws MediaPackageException, SchedulerException {
SchedulerRestService restService = new SchedulerRestService();
List<MediaPackage> mpList = new ArrayList();
mpList.add(generateEvent(Opt.some("mp1")));
mpList.add(generateEvent(Opt.some("mp2")));
String jsonStr = restService.getEventListAsJsonString(mpList);
Assert.assertTrue(jsonStr.contains("mp1"));
Assert.assertTrue(jsonStr.contains("mp2"));
}

private String addDublinCore(Opt<String> id, MediaPackage mediaPackage, final DublinCoreCatalog initalEvent)
throws URISyntaxException, IOException {
String catalogId = UUID.randomUUID().toString();
Expand Down