use of org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter in project opencast by opencast.
the class SeriesWorkflowOperationHandlerTest method setUp.
@Before
public void setUp() throws Exception {
MediaPackageBuilder builder = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder();
mp = builder.loadFromXml(getClass().getResourceAsStream("/series_mediapackage.xml"));
URI uri = getClass().getResource("/dublincore.xml").toURI();
File file = new File(uri);
seriesCatalog = DublinCores.mkOpencast().getCatalog();
seriesCatalog.set(DublinCore.PROPERTY_TITLE, "Series 1");
SeriesService seriesService = EasyMock.createNiceMock(SeriesService.class);
EasyMock.expect(seriesService.getSeries(EasyMock.anyString())).andReturn(seriesCatalog).anyTimes();
EasyMock.expect(seriesService.getSeriesAccessControl(EasyMock.anyString())).andReturn(new AccessControlList()).anyTimes();
EasyMock.expect(seriesService.getSeriesElementData(EasyMock.anyString(), EasyMock.anyString())).andReturn(Opt.some(FileUtils.readFileToByteArray(file))).anyTimes();
EasyMock.replay(seriesService);
SecurityService securityService = EasyMock.createNiceMock(SecurityService.class);
EasyMock.expect(securityService.getOrganization()).andReturn(new DefaultOrganization()).anyTimes();
EasyMock.replay(securityService);
capturedStream = Capture.newInstance(CaptureType.FIRST);
Workspace workspace = EasyMock.createNiceMock(Workspace.class);
EasyMock.expect(workspace.get(EasyMock.anyObject(URI.class))).andReturn(file).anyTimes();
EasyMock.expect(workspace.read(EasyMock.anyObject(URI.class))).andAnswer(() -> getClass().getResourceAsStream("/dublincore.xml")).anyTimes();
EasyMock.expect(workspace.put(EasyMock.anyString(), EasyMock.anyString(), EasyMock.anyString(), EasyMock.capture(capturedStream))).andReturn(uri).anyTimes();
EasyMock.replay(workspace);
AuthorizationService authorizationService = EasyMock.createNiceMock(AuthorizationService.class);
EasyMock.replay(authorizationService);
SeriesCatalogUIAdapter adapter = EasyMock.createNiceMock(SeriesCatalogUIAdapter.class);
EasyMock.expect(adapter.getOrganization()).andReturn(new DefaultOrganization().getId()).anyTimes();
EasyMock.expect(adapter.getFlavor()).andReturn("creativecommons/series").anyTimes();
EasyMock.replay(adapter);
SeriesCatalogUIAdapter seriesAdapter = EasyMock.createNiceMock(SeriesCatalogUIAdapter.class);
EasyMock.expect(seriesAdapter.getOrganization()).andReturn(new DefaultOrganization().getId()).anyTimes();
EasyMock.expect(seriesAdapter.getFlavor()).andReturn("dublincore/series").anyTimes();
EasyMock.replay(seriesAdapter);
// set up the handler
operationHandler = new SeriesWorkflowOperationHandler();
operationHandler.setSeriesService(seriesService);
operationHandler.setSecurityService(securityService);
operationHandler.setWorkspace(workspace);
operationHandler.setAuthorizationService(authorizationService);
operationHandler.addCatalogUIAdapter(adapter);
operationHandler.addCatalogUIAdapter(seriesAdapter);
}
use of org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter in project opencast by opencast.
the class SeriesEndpoint method getSeriesMetadata.
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{seriesId}/metadata.json")
@RestQuery(name = "getseriesmetadata", description = "Returns the series metadata as JSON", returnDescription = "Returns the series metadata as JSON", pathParameters = { @RestParameter(name = "seriesId", isRequired = true, description = "The series identifier", type = STRING) }, reponses = { @RestResponse(responseCode = SC_OK, description = "The series metadata as JSON."), @RestResponse(responseCode = SC_NOT_FOUND, description = "The series has not been found"), @RestResponse(responseCode = SC_UNAUTHORIZED, description = "If the current user is not authorized to perform this action") })
public Response getSeriesMetadata(@PathParam("seriesId") String series) throws UnauthorizedException, NotFoundException, SearchIndexException {
Opt<Series> optSeries = indexService.getSeries(series, searchIndex);
if (optSeries.isNone())
return notFound("Cannot find a series with id '%s'.", series);
MetadataList metadataList = new MetadataList();
List<SeriesCatalogUIAdapter> catalogUIAdapters = indexService.getSeriesCatalogUIAdapters();
catalogUIAdapters.remove(indexService.getCommonSeriesCatalogUIAdapter());
for (SeriesCatalogUIAdapter adapter : catalogUIAdapters) {
final Opt<MetadataCollection> optSeriesMetadata = adapter.getFields(series);
if (optSeriesMetadata.isSome()) {
metadataList.add(adapter.getFlavor(), adapter.getUITitle(), optSeriesMetadata.get());
}
}
metadataList.add(indexService.getCommonSeriesCatalogUIAdapter(), getSeriesMetadata(optSeries.get()));
return okJson(metadataList.toJSON());
}
use of org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter in project opencast by opencast.
the class SeriesEndpoint method deserializeMetadataList.
/**
* Change the simplified fields of key values provided to the external api into a {@link MetadataList}.
*
* @param json
* The json string that contains an array of metadata field lists for the different catalogs.
* @return A {@link MetadataList} with the fields populated with the values provided.
* @throws ParseException
* Thrown if unable to parse the json string.
* @throws NotFoundException
* Thrown if unable to find the catalog or field that the json refers to.
*/
protected MetadataList deserializeMetadataList(String json) throws ParseException, NotFoundException {
MetadataList metadataList = new MetadataList();
JSONParser parser = new JSONParser();
JSONArray jsonCatalogs = (JSONArray) parser.parse(json);
for (int i = 0; i < jsonCatalogs.size(); i++) {
JSONObject catalog = (JSONObject) jsonCatalogs.get(i);
if (catalog.get("flavor") == null || StringUtils.isBlank(catalog.get("flavor").toString())) {
throw new IllegalArgumentException("Unable to create new series as no flavor was given for one of the metadata collections");
}
String flavorString = catalog.get("flavor").toString();
MediaPackageElementFlavor flavor = MediaPackageElementFlavor.parseFlavor(flavorString);
MetadataCollection collection = null;
SeriesCatalogUIAdapter adapter = null;
for (SeriesCatalogUIAdapter seriesCatalogUIAdapter : indexService.getSeriesCatalogUIAdapters()) {
MediaPackageElementFlavor catalogFlavor = MediaPackageElementFlavor.parseFlavor(seriesCatalogUIAdapter.getFlavor());
if (catalogFlavor.equals(flavor)) {
adapter = seriesCatalogUIAdapter;
collection = seriesCatalogUIAdapter.getRawFields();
}
}
if (collection == null) {
throw new IllegalArgumentException(String.format("Unable to find an SeriesCatalogUIAdapter with Flavor '%s'", flavorString));
}
String fieldsJson = catalog.get("fields").toString();
if (StringUtils.trimToNull(fieldsJson) != null) {
Map<String, String> fields = RequestUtils.getKeyValueMap(fieldsJson);
for (String key : fields.keySet()) {
if ("subjects".equals(key)) {
MetadataField<?> field = collection.getOutputFields().get("subject");
if (field == null) {
throw new NotFoundException(String.format("Cannot find a metadata field with id '%s' from Catalog with Flavor '%s'.", key, flavorString));
}
collection.removeField(field);
try {
JSONArray subjects = (JSONArray) parser.parse(fields.get(key));
collection.addField(MetadataField.copyMetadataFieldWithValue(field, StringUtils.join(subjects.iterator(), ",")));
} catch (ParseException e) {
throw new IllegalArgumentException(String.format("Unable to parse the 'subjects' metadata array field because: %s", e.toString()));
}
} else {
MetadataField<?> field = collection.getOutputFields().get(key);
if (field == null) {
throw new NotFoundException(String.format("Cannot find a metadata field with id '%s' from Catalog with Flavor '%s'.", key, flavorString));
}
collection.removeField(field);
collection.addField(MetadataField.copyMetadataFieldWithValue(field, fields.get(key)));
}
}
}
metadataList.add(adapter, collection);
}
return metadataList;
}
use of org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter in project opencast by opencast.
the class SeriesWorkflowOperationHandler method start.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
* JobContext)
*/
@Override
public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException {
logger.debug("Running series workflow operation");
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
Opt<String> optSeries = getOptConfig(workflowInstance.getCurrentOperation(), SERIES_PROPERTY);
Opt<String> optAttachFlavors = getOptConfig(workflowInstance.getCurrentOperation(), ATTACH_PROPERTY);
Boolean applyAcl = getOptConfig(workflowInstance.getCurrentOperation(), APPLY_ACL_PROPERTY).map(toBoolean).getOr(false);
Opt<String> optCopyMetadata = getOptConfig(workflowInstance.getCurrentOperation(), COPY_METADATA_PROPERTY);
String defaultNamespace = getOptConfig(workflowInstance.getCurrentOperation(), DEFAULT_NS_PROPERTY).getOr(DublinCore.TERMS_NS_URI);
logger.debug("Using default namespace: '{}'", defaultNamespace);
if (optSeries.isSome() && !optSeries.get().equals(mediaPackage.getSeries())) {
logger.info("Changing series id from '{}' to '{}'", StringUtils.trimToEmpty(mediaPackage.getSeries()), optSeries.get());
mediaPackage.setSeries(optSeries.get());
}
String seriesId = mediaPackage.getSeries();
if (seriesId == null) {
logger.info("No series set, skip operation");
return createResult(mediaPackage, Action.SKIP);
}
DublinCoreCatalog series;
try {
series = seriesService.getSeries(seriesId);
} catch (NotFoundException e) {
logger.info("No series with the identifier '{}' found, skip operation", seriesId);
return createResult(mediaPackage, Action.SKIP);
} catch (UnauthorizedException e) {
logger.warn("Not authorized to get series with identifier '{}' found, skip operation", seriesId);
return createResult(mediaPackage, Action.SKIP);
} catch (SeriesException e) {
logger.error("Unable to get series with identifier '{}', skip operation: {}", seriesId, ExceptionUtils.getStackTrace(e));
throw new WorkflowOperationException(e);
}
mediaPackage.setSeriesTitle(series.getFirst(DublinCore.PROPERTY_TITLE));
// Process extra metadata
HashSet<EName> extraMetadata = new HashSet<>();
if (optCopyMetadata.isSome()) {
for (String strEName : optCopyMetadata.get().split(",+\\s*")) try {
if (!strEName.isEmpty()) {
extraMetadata.add(EName.fromString(strEName, defaultNamespace));
}
} catch (IllegalArgumentException iae) {
logger.warn("Ignoring incorrect dublincore metadata property: '{}'", strEName);
}
}
// Update the episode catalog
for (Catalog episodeCatalog : mediaPackage.getCatalogs(MediaPackageElements.EPISODE)) {
DublinCoreCatalog episodeDublinCore = DublinCoreUtil.loadDublinCore(workspace, episodeCatalog);
// Make sure the MP catalog has bindings defined
episodeDublinCore.addBindings(XmlNamespaceContext.mk(XmlNamespaceBinding.mk(DublinCore.TERMS_NS_PREFIX, DublinCore.TERMS_NS_URI)));
episodeDublinCore.addBindings(XmlNamespaceContext.mk(XmlNamespaceBinding.mk(DublinCore.ELEMENTS_1_1_NS_PREFIX, DublinCore.ELEMENTS_1_1_NS_URI)));
episodeDublinCore.addBindings(XmlNamespaceContext.mk(XmlNamespaceBinding.mk(DublinCores.OC_PROPERTY_NS_PREFIX, DublinCores.OC_PROPERTY_NS_URI)));
episodeDublinCore.set(DublinCore.PROPERTY_IS_PART_OF, seriesId);
for (EName property : extraMetadata) {
if (!episodeDublinCore.hasValue(property) && series.hasValue(property)) {
episodeDublinCore.set(property, series.get(property));
}
}
try (InputStream in = IOUtils.toInputStream(episodeDublinCore.toXmlString(), "UTF-8")) {
String filename = FilenameUtils.getName(episodeCatalog.getURI().toString());
URI uri = workspace.put(mediaPackage.getIdentifier().toString(), episodeCatalog.getIdentifier(), filename, in);
episodeCatalog.setURI(uri);
// setting the URI to a new source so the checksum will most like be invalid
episodeCatalog.setChecksum(null);
} catch (Exception e) {
logger.error("Unable to update episode catalog isPartOf field: {}", ExceptionUtils.getStackTrace(e));
throw new WorkflowOperationException(e);
}
}
// Attach series catalogs
if (optAttachFlavors.isSome()) {
// Remove existing series catalogs
AbstractMediaPackageElementSelector<Catalog> catalogSelector = new CatalogSelector();
String[] seriesFlavors = StringUtils.split(optAttachFlavors.get(), ",");
for (String flavor : seriesFlavors) {
if ("*".equals(flavor)) {
catalogSelector.addFlavor("*/*");
} else {
catalogSelector.addFlavor(flavor);
}
}
for (Catalog c : catalogSelector.select(mediaPackage, false)) {
if (MediaPackageElements.SERIES.equals(c.getFlavor()) || "series".equals(c.getFlavor().getSubtype())) {
mediaPackage.remove(c);
}
}
List<SeriesCatalogUIAdapter> adapters = getSeriesCatalogUIAdapters();
for (String flavorString : seriesFlavors) {
MediaPackageElementFlavor flavor;
if ("*".equals(flavorString)) {
flavor = MediaPackageElementFlavor.parseFlavor("*/*");
} else {
flavor = MediaPackageElementFlavor.parseFlavor(flavorString);
}
for (SeriesCatalogUIAdapter a : adapters) {
MediaPackageElementFlavor adapterFlavor = MediaPackageElementFlavor.parseFlavor(a.getFlavor());
if (flavor.matches(adapterFlavor)) {
if (MediaPackageElements.SERIES.eq(a.getFlavor())) {
addDublinCoreCatalog(series, MediaPackageElements.SERIES, mediaPackage);
} else {
try {
Opt<byte[]> seriesElementData = seriesService.getSeriesElementData(seriesId, adapterFlavor.getType());
if (seriesElementData.isSome()) {
DublinCoreCatalog catalog = DublinCores.read(new ByteArrayInputStream(seriesElementData.get()));
addDublinCoreCatalog(catalog, adapterFlavor, mediaPackage);
} else {
logger.warn("No extended series catalog found for flavor '{}' and series '{}', skip adding catalog", adapterFlavor.getType(), seriesId);
}
} catch (SeriesException e) {
logger.error("Unable to load extended series metadata for flavor {}", adapterFlavor.getType());
throw new WorkflowOperationException(e);
}
}
}
}
}
}
if (applyAcl) {
try {
AccessControlList acl = seriesService.getSeriesAccessControl(seriesId);
if (acl != null)
authorizationService.setAcl(mediaPackage, AclScope.Series, acl);
} catch (Exception e) {
logger.error("Unable to update series ACL: {}", ExceptionUtils.getStackTrace(e));
throw new WorkflowOperationException(e);
}
}
return createResult(mediaPackage, Action.CONTINUE);
}
use of org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter in project opencast by opencast.
the class SeriesEndpoint method updateSeriesMetadata.
@PUT
@Path("{seriesId}/metadata")
@Produces({ "application/json", "application/v1.0.0+json" })
@RestQuery(name = "updateseriesmetadata", description = "Update a series' metadata of the given type. For a metadata catalog there is the flavor such as 'dublincore/series' and this is the unique type.", returnDescription = "", pathParameters = { @RestParameter(name = "seriesId", description = "The series id", isRequired = true, type = STRING) }, restParameters = { @RestParameter(name = "type", isRequired = true, description = "The type of metadata to update", type = STRING), @RestParameter(name = "metadata", description = "Series metadata as Form param", isRequired = true, type = STRING) }, reponses = { @RestResponse(description = "The series' metadata have been updated.", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "The request is invalid or inconsistent.", responseCode = HttpServletResponse.SC_BAD_REQUEST), @RestResponse(description = "The specified series does not exist.", responseCode = HttpServletResponse.SC_NOT_FOUND) })
public Response updateSeriesMetadata(@HeaderParam("Accept") String acceptHeader, @PathParam("seriesId") String id, @QueryParam("type") String type, @FormParam("metadata") String metadataJSON) throws Exception {
if (StringUtils.trimToNull(metadataJSON) == null) {
return RestUtil.R.badRequest("Unable to update metadata for series as the metadata provided is empty.");
}
Map<String, String> updatedFields;
try {
updatedFields = RequestUtils.getKeyValueMap(metadataJSON);
} catch (ParseException e) {
logger.debug("Unable to update series '{}' with metadata type '{}' and content '{}' because: {}", id, type, metadataJSON, ExceptionUtils.getStackTrace(e));
return RestUtil.R.badRequest(String.format("Unable to parse metadata fields as json from '%s' because '%s'", metadataJSON, ExceptionUtils.getStackTrace(e)));
} catch (IllegalArgumentException e) {
return RestUtil.R.badRequest(e.getMessage());
}
if (updatedFields == null || updatedFields.size() == 0) {
return RestUtil.R.badRequest(String.format("Unable to parse metadata fields as json from '%s' because there were no fields to update.", metadataJSON));
}
Opt<MetadataCollection> optCollection = Opt.none();
SeriesCatalogUIAdapter adapter = null;
Opt<Series> optSeries = indexService.getSeries(id, externalIndex);
if (optSeries.isNone())
return ApiResponses.notFound("Cannot find a series with id '%s'.", id);
MetadataList metadataList = new MetadataList();
// Try the main catalog first as we load it from the index.
if (typeMatchesSeriesCatalogUIAdapter(type, indexService.getCommonSeriesCatalogUIAdapter())) {
optCollection = Opt.some(getSeriesMetadata(optSeries.get()));
adapter = indexService.getCommonSeriesCatalogUIAdapter();
} else {
metadataList.add(indexService.getCommonSeriesCatalogUIAdapter(), getSeriesMetadata(optSeries.get()));
}
// Try the other catalogs
List<SeriesCatalogUIAdapter> catalogUIAdapters = indexService.getSeriesCatalogUIAdapters();
catalogUIAdapters.remove(indexService.getCommonSeriesCatalogUIAdapter());
if (catalogUIAdapters.size() > 0) {
for (SeriesCatalogUIAdapter catalogUIAdapter : catalogUIAdapters) {
if (typeMatchesSeriesCatalogUIAdapter(type, catalogUIAdapter)) {
optCollection = catalogUIAdapter.getFields(id);
adapter = catalogUIAdapter;
} else {
Opt<MetadataCollection> current = catalogUIAdapter.getFields(id);
if (current.isSome()) {
metadataList.add(catalogUIAdapter, current.get());
}
}
}
}
if (optCollection.isNone()) {
return ApiResponses.notFound("Cannot find a catalog with type '%s' for series with id '%s'.", type, id);
}
MetadataCollection collection = optCollection.get();
for (String key : updatedFields.keySet()) {
MetadataField<?> field = collection.getOutputFields().get(key);
if (field == null) {
return ApiResponses.notFound("Cannot find a metadata field with id '%s' from event with id '%s' and the metadata type '%s'.", key, id, type);
} else if (field.isRequired() && StringUtils.isBlank(updatedFields.get(key))) {
return R.badRequest(String.format("The series metadata field with id '%s' and the metadata type '%s' is required and can not be empty!.", key, type));
}
collection.removeField(field);
collection.addField(MetadataField.copyMetadataFieldWithValue(field, updatedFields.get(key)));
}
metadataList.add(adapter, collection);
indexService.updateAllSeriesMetadata(id, metadataList, externalIndex);
return ApiResponses.Json.ok(ApiVersion.VERSION_1_0_0, "");
}
Aggregations