diff --git a/indexing-solr/src/main/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilter.java b/indexing-solr/src/main/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilter.java index e1b2f79974..b89dfc169d 100644 --- a/indexing-solr/src/main/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilter.java +++ b/indexing-solr/src/main/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilter.java @@ -54,7 +54,7 @@ private List determineContentStatus(DocumentIndexingPackage dip) status.add(FacetConstants.IS_PRIMARY_OBJECT); } if (parentResc.hasProperty(Cdr.useAsThumbnail, resc)) { - status.add(FacetConstants.IS_ASSIGNED_THUMBNAIL); + status.add(FacetConstants.ASSIGNED_AS_THUMBNAIL); } } @@ -75,7 +75,7 @@ private void addWorkObjectStatuses(List status, Resource resource) { } if (resource.hasProperty(Cdr.useAsThumbnail)) { - status.add(FacetConstants.THUMBNAIL_ASSIGNED); + status.add(FacetConstants.HAS_THUMBNAIL_ASSIGNED); } else { status.add(FacetConstants.NO_THUMBNAIL_ASSIGNED); } diff --git a/indexing-solr/src/test/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilterTest.java b/indexing-solr/src/test/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilterTest.java index 8796c2ad07..5cc21ddd74 100644 --- a/indexing-solr/src/test/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilterTest.java +++ b/indexing-solr/src/test/java/edu/unc/lib/boxc/indexing/solr/filter/SetContentStatusFilterTest.java @@ -190,7 +190,7 @@ public void testWorkWithAssignedThumbnail() throws Exception { filter.filter(dip); verify(idb).setContentStatus(listCaptor.capture()); - assertTrue(listCaptor.getValue().contains(FacetConstants.THUMBNAIL_ASSIGNED)); + assertTrue(listCaptor.getValue().contains(FacetConstants.HAS_THUMBNAIL_ASSIGNED)); assertFalse(listCaptor.getValue().contains(FacetConstants.NO_THUMBNAIL_ASSIGNED)); } @@ -216,6 +216,6 @@ public void testIsAssignedThumbnail() throws Exception { filter.filter(dip); verify(idb).setContentStatus(listCaptor.capture()); - assertTrue(listCaptor.getValue().contains(FacetConstants.IS_ASSIGNED_THUMBNAIL)); + assertTrue(listCaptor.getValue().contains(FacetConstants.ASSIGNED_AS_THUMBNAIL)); } } diff --git a/search-api/src/main/java/edu/unc/lib/boxc/search/api/FacetConstants.java b/search-api/src/main/java/edu/unc/lib/boxc/search/api/FacetConstants.java index a6ffe9d126..c5ccf7a509 100644 --- a/search-api/src/main/java/edu/unc/lib/boxc/search/api/FacetConstants.java +++ b/search-api/src/main/java/edu/unc/lib/boxc/search/api/FacetConstants.java @@ -23,8 +23,8 @@ public abstract class FacetConstants { public static final String STAFF_ONLY_ACCESS = "Staff-Only Access"; public static final String MEMBERS_ARE_ORDERED = "Members Are Ordered"; public static final String MEMBERS_ARE_UNORDERED = "Members Are Unordered"; - public static final String THUMBNAIL_ASSIGNED = "Has Assigned Thumbnail"; + public static final String HAS_THUMBNAIL_ASSIGNED = "Has Assigned Thumbnail"; public static final String NO_THUMBNAIL_ASSIGNED = "No Assigned Thumbnail"; - public static final String IS_ASSIGNED_THUMBNAIL = "Is Assigned Thumbnail"; + public static final String ASSIGNED_AS_THUMBNAIL = "Assigned As Thumbnail"; } diff --git a/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailRequestProcessor.java b/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailRequestProcessor.java index 100df0db0f..98d28c3006 100644 --- a/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailRequestProcessor.java +++ b/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailRequestProcessor.java @@ -3,6 +3,7 @@ import edu.unc.lib.boxc.auth.api.Permission; import edu.unc.lib.boxc.auth.api.services.AccessControlService; import edu.unc.lib.boxc.model.api.objects.RepositoryObjectLoader; +import edu.unc.lib.boxc.model.api.objects.WorkObject; import edu.unc.lib.boxc.model.api.rdf.Cdr; import edu.unc.lib.boxc.model.api.services.RepositoryObjectFactory; import edu.unc.lib.boxc.model.fcrepo.ids.PIDs; @@ -39,10 +40,17 @@ public void process(Exchange exchange) throws IOException { pid, agent.getPrincipals(), Permission.editDescription); var file = repositoryObjectLoader.getFileObject(pid); - var work = file.getParent(); + var work = (WorkObject) file.getParent(); if (Objects.equals(action, ThumbnailRequest.ASSIGN)) { + // Capture the old thumbnail id before it gets cleared + var oldThumbnailFile = work.getThumbnailObject(); repositoryObjectFactory.createExclusiveRelationship(work, Cdr.useAsThumbnail, file.getResource()); + // reindex old thumbnail object + if (oldThumbnailFile != null) { + indexingMessageSender.sendIndexingOperation( + agent.getUsername(), oldThumbnailFile.getPid(), IndexingActionType.UPDATE_DATASTREAMS); + } } else if ( Objects.equals(action, ThumbnailRequest.DELETE)) { repositoryObjectFactory.deleteProperty(work, Cdr.useAsThumbnail); } @@ -50,6 +58,8 @@ public void process(Exchange exchange) throws IOException { // send message to update solr indexingMessageSender.sendIndexingOperation( agent.getUsername(), work.getPid(), IndexingActionType.UPDATE_DATASTREAMS); + indexingMessageSender.sendIndexingOperation( + agent.getUsername(), file.getPid(), IndexingActionType.UPDATE_DATASTREAMS); } public void setRepositoryObjectLoader(RepositoryObjectLoader repositoryObjectLoader) { diff --git a/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailProcessorTest.java b/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailProcessorTest.java index 5f074584ab..62931e6f8a 100644 --- a/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailProcessorTest.java +++ b/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/thumbnails/ThumbnailProcessorTest.java @@ -87,14 +87,37 @@ void closeService() throws Exception { } @Test - public void testUpdateThumbnail() throws Exception { + public void testAssignThumbnail() throws Exception { var exchange = createRequestExchange(ThumbnailRequest.ASSIGN); processor.process(exchange); verify(repositoryObjectFactory).createExclusiveRelationship(eq(parentWork), eq(Cdr.useAsThumbnail), fileResourceCaptor.capture()); // check to see the right file was related to the work assertEquals(resource, fileResourceCaptor.getValue()); - assertIndexingMessageSent(); + assertIndexingMessageSent(workPid); + assertIndexingMessageSent(filePid); + } + + // test for when the work already has an assigned thumbnail and a new one is chosen + @Test + public void testAssignNewThumbnail() throws Exception { + // set up pre-existing assigned thumbnail + var oldThumbnailPid = ProcessorTestHelper.makePid(); + var oldThumbnailFile = mock(FileObject.class); + var oldResource = mock(Resource.class); + when(oldThumbnailFile.getResource()).thenReturn(oldResource); + when(oldThumbnailFile.getPid()).thenReturn(oldThumbnailPid); + when(parentWork.getThumbnailObject()).thenReturn(oldThumbnailFile); + + var exchange = createRequestExchange(ThumbnailRequest.ASSIGN); + + processor.process(exchange); + verify(repositoryObjectFactory).createExclusiveRelationship(eq(parentWork), eq(Cdr.useAsThumbnail), fileResourceCaptor.capture()); + // check to see the right file was related to the work + assertEquals(resource, fileResourceCaptor.getValue()); + assertIndexingMessageSent(workPid); + assertIndexingMessageSent(filePid); + assertIndexingMessageSent(oldThumbnailPid); } @Test @@ -114,11 +137,11 @@ public void testDeleteAssignedThumbnail() throws IOException { processor.process(exchange); verify(repositoryObjectFactory).deleteProperty(eq(parentWork), eq(Cdr.useAsThumbnail)); - assertIndexingMessageSent(); + assertIndexingMessageSent(workPid); } - private void assertIndexingMessageSent() { - verify(indexingMessageSender).sendIndexingOperation(agent.getUsername(), workPid, + private void assertIndexingMessageSent(PID pid) { + verify(indexingMessageSender).sendIndexingOperation(agent.getUsername(), pid, IndexingActionType.UPDATE_DATASTREAMS); } diff --git a/static/js/admin/src/ResultObject.js b/static/js/admin/src/ResultObject.js index fb9414a937..f7bc8b70f3 100644 --- a/static/js/admin/src/ResultObject.js +++ b/static/js/admin/src/ResultObject.js @@ -31,7 +31,7 @@ define('ResultObject', [ 'jquery', 'jquery-ui', 'underscore', 'ModalLoadingOverl if (tags.length > 0) { this.metadata.tags = tags.filter(function(d) { - return !/^(has|not|no.primary|public.access|members.are.unordered)/i.test(d); + return !/^(has|not|no.primary|public.access|members.are.unordered|no.assigned.thumbnail)/i.test(d); }).map(function(d) { var tagValue; @@ -85,7 +85,7 @@ define('ResultObject', [ 'jquery', 'jquery-ui', 'underscore', 'ModalLoadingOverl case 'patron-settings': helpText = 'Patron access settings for this object have been added'; break; - case 'primary-object': + case 'is-primary-object': helpText = 'This file is the representative object for the work which contains it'; break; case 'staff-only': @@ -94,6 +94,9 @@ define('ResultObject', [ 'jquery', 'jquery-ui', 'underscore', 'ModalLoadingOverl case 'inherited-settings': helpText = 'Object is inheriting patron access settings which have been modified (typically they are more restrictive)'; break; + case 'assigned-as-thumbnail': + helpText = 'This file is the assigned thumbnail for the work which contains it'; + break; default: helpText = ''; } diff --git a/static/js/admin/src/ResultObjectActionMenu.js b/static/js/admin/src/ResultObjectActionMenu.js index afca876247..3befc524d6 100644 --- a/static/js/admin/src/ResultObjectActionMenu.js +++ b/static/js/admin/src/ResultObjectActionMenu.js @@ -151,10 +151,18 @@ define('ResultObjectActionMenu', [ 'jquery', 'jquery-ui', 'StringUtilities', 'A } else { items['setAsPrimaryObject'] = { name : 'Set as Primary Object' }; } + if ($.inArray('Assigned As Thumbnail', metadata.contentStatus) != -1) { + items['clearAssignedThumbnail'] = { name : 'Clear Assigned Thumbnail' }; + } else { + items['assignAsThumbnail'] = { name : 'Assign as Thumbnail' }; + } } else if (metadata.type == 'Work') { if ($.inArray('Has Primary Object', metadata.contentStatus) != -1) { items['clearPrimaryObject'] = { name : 'Clear Primary Object' }; } + if ($.inArray('Has Assigned Thumbnail', metadata.contentStatus) != -1) { + items['clearAssignedThumbnail'] = { name : 'Clear Assigned Thumbnail' }; + } } } @@ -363,6 +371,20 @@ define('ResultObjectActionMenu', [ 'jquery', 'jquery-ui', 'StringUtilities', 'A confirm : false }); break; + case "clearAssignedThumbnail" : + self.actionHandler.addEvent({ + action : 'ClearAssignedThumbnail', + target : resultObject, + confirm : false + }); + break; + case "assignAsThumbnail" : + self.actionHandler.addEvent({ + action : 'AssignAsThumbnail', + target : resultObject, + confirm : false + }); + break; case "destroy" : self.actionHandler.addEvent({ action : 'DestroyResult', diff --git a/static/js/admin/src/action/AssignAsThumbnailAction.js b/static/js/admin/src/action/AssignAsThumbnailAction.js new file mode 100644 index 0000000000..f19b47d493 --- /dev/null +++ b/static/js/admin/src/action/AssignAsThumbnailAction.js @@ -0,0 +1,64 @@ +define('AssignAsThumbnailAction', [ 'jquery', 'AjaxCallbackAction'], function($, AjaxCallbackAction) { + + function AssignAsThumbnailAction(context) { + this._create(context); + }; + + AssignAsThumbnailAction.prototype.constructor = AssignAsThumbnailAction; + AssignAsThumbnailAction.prototype = Object.create( AjaxCallbackAction.prototype ); + + AssignAsThumbnailAction.prototype._create = function(context) { + this.context = context; + + var options = { + workMethod: "PUT", + workPath: "/services/api/edit/assignThumbnail/{idPath}", + workLabel: "Setting as assigned thumbnail...", + followupLabel: "Setting as assigned thumbnail...", + followupPath: "/services/api/status/item/{idPath}/solrRecord/version" + } + + if ('confirm' in this.context && !this.context.confirm) { + options.confirm = false; + } else { + options.confirm = { + promptText : "Use this as the assigned thumbnail for its parent?", + confirmAnchor : this.context.confirmAnchor + }; + } + + AjaxCallbackAction.prototype._create.call(this, options); + }; + + AssignAsThumbnailAction.prototype.completeState = function() { + this.context.actionHandler.addEvent({ + action : 'RefreshResult', + target : this.context.target + }); + this.alertHandler.alertHandler("success", "Assignment of object \"" + this.context.target.metadata.title + "\" as the assigned thumbnail has completed."); + this.context.target.enable(); + }; + + AssignAsThumbnailAction.prototype.workDone = function(data) { + this.completeTimestamp = data.timestamp; + this.oldThumbnailId = data.oldThumbnailId; + this.newThumbnailId = data.newThumbnailId; + if (this.oldThumbnailId) { + this.context.actionHandler.addEvent({ + action : 'RefreshResult', + target : this.context.resultTable.resultObjectList.getResultObject(this.oldThumbnailId), + waitForUpdate : true + }); + } + return true; + }; + + AssignAsThumbnailAction.prototype.followup = function(data) { + if (data) { + return this.context.target.updateVersion(data); + } + return false; + }; + + return AssignAsThumbnailAction; +}); diff --git a/static/js/admin/src/action/ClearAssignedThumbnailAction.js b/static/js/admin/src/action/ClearAssignedThumbnailAction.js new file mode 100644 index 0000000000..6330487248 --- /dev/null +++ b/static/js/admin/src/action/ClearAssignedThumbnailAction.js @@ -0,0 +1,66 @@ +define('ClearAssignedThumbnailAction', [ 'jquery', 'AjaxCallbackAction'], function($, AjaxCallbackAction) { + + function ClearAssignedThumbnailAction(context) { + this._create(context); + }; + + ClearAssignedThumbnailAction.prototype.constructor = ClearAssignedThumbnailAction; + ClearAssignedThumbnailAction.prototype = Object.create( AjaxCallbackAction.prototype ); + + ClearAssignedThumbnailAction.prototype._create = function(context) { + this.context = context; + + var options = { + workMethod: "DELETE", + workPath: "/services/api/edit/deleteThumbnail/{idPath}", + workLabel: "Clearing assigned thumbnail...", + followupLabel: "Clearing assigned thumbnail...", + followupPath: "/services/api/status/item/{idPath}/solrRecord/version" + } + + if ('confirm' in this.context && !this.context.confirm) { + options.confirm = false; + } else { + options.confirm = { + promptText : "Clear the assigned thumbnail?", + confirmAnchor : this.context.confirmAnchor + }; + } + + AjaxCallbackAction.prototype._create.call(this, options); + }; + + ClearAssignedThumbnailAction.prototype.completeState = function() { + this.context.actionHandler.addEvent({ + action : 'RefreshResult', + target : this.context.target + }); + this.alertHandler.alertHandler("success", "Cleared the assigned thumbnail."); + this.context.target.enable(); + }; + + ClearAssignedThumbnailAction.prototype.workDone = function(data) { + this.completeTimestamp = data.timestamp; + this.oldThumbnailId = data.oldThumbnailId; + if (this.context.target.metadata.type == "Work") { + var oldThumbnail = this.context.resultTable.resultObjectList.getResultObject(this.oldThumbnailId); + if (oldThumbnail != null) { + this.context.actionHandler.addEvent({ + action : 'RefreshResult', + target : oldThumbnail, + waitForUpdate : true + }); + } + } + return true; + }; + + ClearAssignedThumbnailAction.prototype.followup = function(data) { + if (data) { + return this.context.target.updateVersion(data); + } + return false; + }; + + return ClearAssignedThumbnailAction; +}); \ No newline at end of file diff --git a/static/js/admin/src/action/ClearPrimaryObjectResultAction.js b/static/js/admin/src/action/ClearPrimaryObjectResultAction.js index 8fe6e21c1d..8284eaceb9 100644 --- a/static/js/admin/src/action/ClearPrimaryObjectResultAction.js +++ b/static/js/admin/src/action/ClearPrimaryObjectResultAction.js @@ -38,6 +38,13 @@ define('ClearPrimaryObjectResultAction', [ 'jquery', 'AjaxCallbackAction'], func this.alertHandler.alertHandler("success", "Cleared the primary object assignment."); this.context.target.enable(); }; + + ClearPrimaryObjectResultAction.prototype.followup = function(data) { + if (data) { + return this.context.target.updateVersion(data); + } + return false; + }; return ClearPrimaryObjectResultAction; }); \ No newline at end of file diff --git a/static/js/admin/src/action/SetAsPrimaryObjectResultAction.js b/static/js/admin/src/action/SetAsPrimaryObjectResultAction.js index 37ab7064d5..513a3745f6 100644 --- a/static/js/admin/src/action/SetAsPrimaryObjectResultAction.js +++ b/static/js/admin/src/action/SetAsPrimaryObjectResultAction.js @@ -38,6 +38,13 @@ define('SetAsPrimaryObjectResultAction', [ 'jquery', 'AjaxCallbackAction'], func this.alertHandler.alertHandler("success", "Assignment of object \"" + this.context.target.metadata.title + "\" as primary object has completed."); this.context.target.enable(); }; + + SetAsPrimaryObjectResultAction.prototype.followup = function(data) { + if (data) { + return this.context.target.updateVersion(data); + } + return false; + }; return SetAsPrimaryObjectResultAction; }); \ No newline at end of file diff --git a/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailController.java b/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailController.java index 94441bb031..c29024188c 100644 --- a/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailController.java +++ b/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailController.java @@ -11,6 +11,7 @@ import edu.unc.lib.boxc.model.api.ids.PID; import edu.unc.lib.boxc.model.api.objects.FileObject; import edu.unc.lib.boxc.model.api.objects.RepositoryObjectLoader; +import edu.unc.lib.boxc.model.api.objects.WorkObject; import edu.unc.lib.boxc.model.fcrepo.ids.PIDs; import edu.unc.lib.boxc.operations.jms.thumbnails.ThumbnailRequest; import edu.unc.lib.boxc.operations.jms.thumbnails.ThumbnailRequestSender; @@ -46,6 +47,8 @@ @Controller public class ThumbnailController { private static final Logger log = LoggerFactory.getLogger(ThumbnailController.class); + public static final String OLD_THUMBNAIL_ID = "oldThumbnailId"; + public static final String ACTION = "action"; @Autowired private ImportThumbnailService service; @@ -71,7 +74,7 @@ ResponseEntity importThumbnail(@PathVariable("pid") String pid, String mimeType = thumbnailFile.getContentType(); Map result = new HashMap<>(); - result.put("action", "editThumbnail"); + result.put(ACTION, "editThumbnail"); result.put("username", agent.getUsername()); try (InputStream importStream = thumbnailFile.getInputStream()) { @@ -100,6 +103,8 @@ ResponseEntity importThumbnail(@PathVariable("pid") String pid, @ResponseBody public ResponseEntity assignThumbnail(@PathVariable("pidString") String pidString) { PID pid = PIDs.get(pidString); + Map result = new HashMap<>(); + result.put(ACTION, "assignThumbnail"); AccessGroupSet principals = getAgentPrincipals().getPrincipals(); aclService.assertHasAccess("Insufficient permissions to assign thumbnail for " + pidString, @@ -111,6 +116,12 @@ public ResponseEntity assignThumbnail(@PathVariable("pidString") String return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } + var workObject = (WorkObject) object.getParent(); + var oldThumbnail = workObject.getThumbnailObject(); + if (oldThumbnail != null) { + result.put(OLD_THUMBNAIL_ID, oldThumbnail.getPid().getId()); + } + var agent = AgentPrincipalsImpl.createFromThread(); var request = new ThumbnailRequest(); request.setAgent(agent); @@ -123,29 +134,46 @@ public ResponseEntity assignThumbnail(@PathVariable("pidString") String log.error("Error assigning file {} as thumbnail", request.getFilePidString(), e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } - return new ResponseEntity<>(HttpStatus.OK); + result.put("timestamp", System.currentTimeMillis()); + result.put("newThumbnailId", object.getPid().getId()); + return new ResponseEntity<>(result, HttpStatus.OK); } @DeleteMapping(value = "/edit/deleteThumbnail/{pidString}") @ResponseBody public ResponseEntity deleteThumbnail(@PathVariable("pidString") String pidString) { PID pid = PIDs.get(pidString); + Map result = new HashMap<>(); + result.put(ACTION, "deleteThumbnail"); AccessGroupSet principals = getAgentPrincipals().getPrincipals(); aclService.assertHasAccess("Insufficient permissions to assign thumbnail for " + pidString, pid, principals, Permission.editDescription); var object = repositoryObjectLoader.getRepositoryObject(pid); - if (!(object instanceof FileObject)) { - log.error("Error object is not a file: {}", pidString); + if (!(object instanceof FileObject) && !(object instanceof WorkObject)) { + log.error("Error object is not a file or work: {}", pidString); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + if (object instanceof WorkObject && ((WorkObject) object).getThumbnailObject() == null) { + log.error("Error work object does not have assigned thumbnail to delete: {}", pidString); return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } var agent = AgentPrincipalsImpl.createFromThread(); var request = new ThumbnailRequest(); request.setAgent(agent); - request.setFilePidString(pidString); request.setAction(ThumbnailRequest.DELETE); + if (object instanceof WorkObject) { + var fileId = ((WorkObject) object).getThumbnailObject().getPid().getId(); + request.setFilePidString(fileId); + result.put(OLD_THUMBNAIL_ID, fileId); + } else { + request.setFilePidString(pidString); + result.put(OLD_THUMBNAIL_ID, object.getPid().getId()); + } + try { thumbnailRequestSender.sendToQueue(request); } catch (IOException e) { @@ -153,6 +181,7 @@ public ResponseEntity deleteThumbnail(@PathVariable("pidString") String return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } - return new ResponseEntity<>(HttpStatus.OK); + result.put("timestamp", System.currentTimeMillis()); + return new ResponseEntity<>(result, HttpStatus.OK); } } diff --git a/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailIT.java b/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailIT.java index c265575a7f..4a5e68eda5 100644 --- a/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailIT.java +++ b/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/modify/ThumbnailIT.java @@ -21,8 +21,10 @@ import java.io.File; import java.io.FileInputStream; import java.nio.file.Path; +import java.util.Map; import edu.unc.lib.boxc.model.api.objects.RepositoryObjectLoader; +import edu.unc.lib.boxc.model.api.rdf.Cdr; import edu.unc.lib.boxc.operations.jms.thumbnails.ThumbnailRequest; import edu.unc.lib.boxc.operations.jms.thumbnails.ThumbnailRequestSender; import org.apache.commons.io.IOUtils; @@ -39,6 +41,7 @@ import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import edu.unc.lib.boxc.auth.api.exceptions.AccessRestrictionException; @@ -163,15 +166,21 @@ public void assignThumbnailSuccess() throws Exception { var pid = makePid(); var filePidString = pid.getId(); var file = repositoryObjectFactory.createFileObject(pid, null); + var work = repositoryObjectFactory.createWorkObject(makePid(), null); when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(file); + work.addMember(file); - mvc.perform(put("/edit/assignThumbnail/" + filePidString)) + MvcResult result = mvc.perform(put("/edit/assignThumbnail/" + filePidString)) .andExpect(status().is2xxSuccessful()) .andReturn(); verify(thumbnailRequestSender).sendToQueue(requestCaptor.capture()); ThumbnailRequest request = requestCaptor.getValue(); assertEquals(filePidString, request.getFilePidString()); + + Map respMap = getMapFromResponse(result); + assertEquals("assignThumbnail", respMap.get(ThumbnailController.ACTION)); + assertEquals(filePidString, respMap.get("newThumbnailId")); } @Test @@ -197,13 +206,40 @@ public void assignThumbnailInvalidPidString() throws Exception { } @Test - public void assignThumbnailPidIsNotAFile() throws Exception { + public void assignThumbnailPidIsAWorkWithPreviousAssignedThumbnail() throws Exception { var pid = makePid(); var filePidString = pid.getId(); - var work = repositoryObjectFactory.createWorkObject(pid, null); + var file = repositoryObjectFactory.createFileObject(pid, null); + var oldThumbnailPid = makePid(); + var oldThumbnail = repositoryObjectFactory.createFileObject(oldThumbnailPid, null); + var workPid = makePid(); + var work = repositoryObjectFactory.createWorkObject(workPid, null); + work.addMember(file); + work.addMember(oldThumbnail); when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(work); + repositoryObjectFactory.createExclusiveRelationship(work, Cdr.useAsThumbnail, oldThumbnail.getResource()); - mvc.perform(put("/edit/assignThumbnail/" + filePidString)) + MvcResult result = mvc.perform(put("/edit/assignThumbnail/" + filePidString)) + .andExpect(status().is2xxSuccessful()) + .andReturn(); + verify(thumbnailRequestSender).sendToQueue(requestCaptor.capture()); + ThumbnailRequest request = requestCaptor.getValue(); + assertEquals(filePidString, request.getFilePidString()); + + Map respMap = getMapFromResponse(result); + assertEquals("assignThumbnail", respMap.get(ThumbnailController.ACTION)); + assertEquals(filePidString, respMap.get("newThumbnailId")); + assertEquals(oldThumbnailPid.getId(), respMap.get(ThumbnailController.OLD_THUMBNAIL_ID)); + } + + @Test + public void assignThumbnailPidIsNotAFileOrWork() throws Exception { + var pid = makePid(); + var folderPidString = pid.getId(); + var folder = repositoryObjectFactory.createFolderObject(pid, null); + when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(folder); + + mvc.perform(put("/edit/assignThumbnail/" + folderPidString)) .andExpect(status().isBadRequest()) .andReturn(); verify(thumbnailRequestSender, never()).sendMessage(any(Document.class)); @@ -215,7 +251,7 @@ public void deleteThumbnailSuccess() throws Exception { var filePidString = pid.getId(); var file = repositoryObjectFactory.createFileObject(pid, null); when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(file); - mvc.perform(delete("/edit/deleteThumbnail/" + filePidString)) + MvcResult result = mvc.perform(delete("/edit/deleteThumbnail/" + filePidString)) .andExpect(status().is2xxSuccessful()) .andReturn(); @@ -223,6 +259,10 @@ public void deleteThumbnailSuccess() throws Exception { ThumbnailRequest request = requestCaptor.getValue(); assertEquals(filePidString, request.getFilePidString()); assertEquals(ThumbnailRequest.DELETE, request.getAction()); + + Map respMap = getMapFromResponse(result); + assertEquals("deleteThumbnail", respMap.get(ThumbnailController.ACTION)); + assertEquals(filePidString, respMap.get(ThumbnailController.OLD_THUMBNAIL_ID)); } @Test @@ -240,10 +280,33 @@ public void deleteThumbnailNoAccess() throws Exception { } @Test - public void deleteThumbnailPidIsNotAFile() throws Exception { + public void deleteThumbnailPidIsAWork() throws Exception { + var pid = makePid(); + var workPidString = pid.getId(); + var work = repositoryObjectFactory.createWorkObject(pid, null); + var filePid = makePid(); + var file = repositoryObjectFactory.createFileObject(filePid, null); + work.addMember(file); + repositoryObjectFactory.createExclusiveRelationship(work, Cdr.useAsThumbnail, file.getResource()); + when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(work); + + mvc.perform(delete("/edit/deleteThumbnail/" + workPidString)) + .andExpect(status().is2xxSuccessful()) + .andReturn(); + verify(thumbnailRequestSender).sendToQueue(requestCaptor.capture()); + ThumbnailRequest request = requestCaptor.getValue(); + assertEquals(filePid.getId(), request.getFilePidString()); + assertEquals(ThumbnailRequest.DELETE, request.getAction()); + } + + @Test + public void deleteThumbnailPidIsAWorkButNoAssignedThumbnail() throws Exception { var pid = makePid(); var workPidString = pid.getId(); var work = repositoryObjectFactory.createWorkObject(pid, null); + var filePid = makePid(); + var file = repositoryObjectFactory.createFileObject(filePid, null); + work.addMember(file); when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(work); mvc.perform(delete("/edit/deleteThumbnail/" + workPidString)) @@ -252,6 +315,19 @@ public void deleteThumbnailPidIsNotAFile() throws Exception { verify(thumbnailRequestSender, never()).sendMessage(any(Document.class)); } + @Test + public void deleteThumbnailPidIsNotAFileOrWork() throws Exception { + var pid = makePid(); + var folderPidString = pid.getId(); + var folder = repositoryObjectFactory.createFolderObject(pid, null); + when(repositoryObjectLoader.getRepositoryObject(pid)).thenReturn(folder); + + mvc.perform(delete("/edit/deleteThumbnail/" + folderPidString)) + .andExpect(status().isBadRequest()) + .andReturn(); + verify(thumbnailRequestSender, never()).sendMessage(any(Document.class)); + } + private byte[] textStream() { return "I am not an image".getBytes(); }