diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definition.lib.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definition.lib.ftl index 5e1d7226ad..efbfc2efa8 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definition.lib.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definition.lib.ftl @@ -4,18 +4,20 @@ { "name": "${replicationDefinition.name}", "status" : "${replicationDefinition.status}", - "enabled" : "${replicationDefinition.enabled}", - "details": "${replicationDefinition.details_url}", + "startedAt" : <#if replicationDefinition.startedAt??>"${replicationDefinition.startedAt}"<#else>null, + "enabled" : ${replicationDefinition.enabled?string}, + "details": "${"/api/replication-definition/" + replicationDefinition.name}", } + <#-- Renders the details of a replication definition. --> <#macro replicationDefinitionJSON replicationDefinition> <#escape x as jsonUtils.encodeJSONString(x)> { "name": "${replicationDefinition.name}", "status" : "${replicationDefinition.status}", - "enabled" : "${replicationDefinition.enabled}", + "enabled" : ${replicationDefinition.enabled?string}, <#-- TODO The rest of the fields --> } diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definitions.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definitions.get.desc.xml index 8fa58efe57..c485e3d7c0 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definitions.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/replication/replication-definitions.get.desc.xml @@ -3,7 +3,7 @@ Returns a simple representation of all persisted replication definitions. - /api/replication-definitions + /api/replication-definitions?sort={sort?} admin required diff --git a/source/java/org/alfresco/repo/web/scripts/replication/AbstractReplicationWebscript.java b/source/java/org/alfresco/repo/web/scripts/replication/AbstractReplicationWebscript.java index 4ed15356d5..c6c6d49d73 100644 --- a/source/java/org/alfresco/repo/web/scripts/replication/AbstractReplicationWebscript.java +++ b/source/java/org/alfresco/repo/web/scripts/replication/AbstractReplicationWebscript.java @@ -18,14 +18,9 @@ */ package org.alfresco.repo.web.scripts.replication; -import java.util.List; import java.util.Map; -import org.alfresco.repo.action.ActionTrackingServiceImpl; import org.alfresco.service.cmr.action.ActionTrackingService; -import org.alfresco.service.cmr.action.ExecutionDetails; -import org.alfresco.service.cmr.action.ExecutionSummary; -import org.alfresco.service.cmr.replication.ReplicationDefinition; import org.alfresco.service.cmr.replication.ReplicationService; import org.alfresco.service.cmr.repository.NodeService; import org.springframework.extensions.webscripts.Cache; diff --git a/source/java/org/alfresco/repo/web/scripts/replication/ReplicationDefinitionsGet.java b/source/java/org/alfresco/repo/web/scripts/replication/ReplicationDefinitionsGet.java index e7841f3a3a..0eccc0261c 100644 --- a/source/java/org/alfresco/repo/web/scripts/replication/ReplicationDefinitionsGet.java +++ b/source/java/org/alfresco/repo/web/scripts/replication/ReplicationDefinitionsGet.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.web.scripts.replication; +import java.util.Comparator; import java.util.List; import java.util.Map; @@ -40,7 +41,19 @@ public class ReplicationDefinitionsGet extends AbstractReplicationWebscript // Get all the defined replication definitions List definitions = replicationService.loadReplicationDefinitions(); + // How do we need to sort them? + Comparator> sorter = new ReplicationModelBuilder.SimpleSorterByName(); + String sort = req.getParameter("sort"); + if(sort == null) { + // Default was set above + } else if(sort.equalsIgnoreCase("status")) { + sorter = new ReplicationModelBuilder.SimpleSorterByStatus(); + } else if(sort.equalsIgnoreCase("lastRun") || + sort.equalsIgnoreCase("lastRunTime")) { + sorter = new ReplicationModelBuilder.SimpleSorterByLastRun(); + } + // Have them turned into simple models - return modelBuilder.buildSimpleList(definitions); + return modelBuilder.buildSimpleList(definitions, sorter); } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/replication/ReplicationModelBuilder.java b/source/java/org/alfresco/repo/web/scripts/replication/ReplicationModelBuilder.java index 634837a582..071f010401 100644 --- a/source/java/org/alfresco/repo/web/scripts/replication/ReplicationModelBuilder.java +++ b/source/java/org/alfresco/repo/web/scripts/replication/ReplicationModelBuilder.java @@ -19,6 +19,9 @@ package org.alfresco.repo.web.scripts.replication; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,6 +32,7 @@ import org.alfresco.service.cmr.action.ExecutionSummary; import org.alfresco.service.cmr.replication.ReplicationDefinition; import org.alfresco.service.cmr.replication.ReplicationService; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.util.ISO8601DateFormat; /** * Builds up models from ReplicationDefinitions, either @@ -46,7 +50,6 @@ public class ReplicationModelBuilder protected static final String DEFINITION_STARTED_AT = "startedAt"; protected static final String DEFINITION_ENDED_AT = "endedAt"; protected static final String DEFINITION_ENABLED = "enabled"; - protected static final String DEFINITION_DETAILS_URL = "details"; protected NodeService nodeService; protected ReplicationService replicationService; @@ -61,11 +64,67 @@ public class ReplicationModelBuilder } + /** + * Sorts simple definitions by their status + */ + public static class SimpleSorterByStatus implements Comparator> { + private SimpleSorterByName nameSorter = new SimpleSorterByName(); + public int compare(Map simpleA, Map simpleB) { + String statusA = (String)simpleA.get(DEFINITION_STATUS); + String statusB = (String)simpleB.get(DEFINITION_STATUS); + if(statusA == null || statusB == null) { + throw new IllegalArgumentException("Status missing during sort"); + } + + int compare = statusA.compareTo(statusB); + if(compare == 0) { + return nameSorter.compare(simpleA, simpleB); + } + return compare; + } + } + /** + * Sorts simple definitions by their name + */ + public static class SimpleSorterByName implements Comparator> { + public int compare(Map simpleA, Map simpleB) { + String nameA = (String)simpleA.get(DEFINITION_NAME); + String nameB = (String)simpleB.get(DEFINITION_NAME); + if(nameA == null || nameB == null) { + throw new IllegalArgumentException("Name missing during sort"); + } + return nameA.compareTo(nameB); + } + } + /** + * Sorts simple definitions by their last run time. + */ + public static class SimpleSorterByLastRun implements Comparator> { + /** Works on ISO8601 formatted date strings */ + public int compare(Map simpleA, Map simpleB) { + String dateA = (String)simpleA.get(DEFINITION_STARTED_AT); + String dateB = (String)simpleB.get(DEFINITION_STARTED_AT); + if(dateA == null && dateB == null) { + return 0; + } + if(dateA != null && dateB == null) { + return 1; + } + if(dateA == null && dateB != null) { + return -1; + } + // We want more recent dates first + return 0-dateA.compareTo(dateB); + } + } + + /** * Build a model containing a list of simple definitions for the given * list of Replication Definitions. */ - protected Map buildSimpleList(List replicationDefinitions) + protected Map buildSimpleList(List replicationDefinitions, + Comparator> sorter) { List> models = new ArrayList>(); @@ -82,17 +141,22 @@ public class ReplicationModelBuilder // Set the core details Map rdm = new HashMap(); rdm.put(DEFINITION_NAME, rd.getReplicationName()); - rdm.put(DEFINITION_ENABLED, rd.isEnabled()); - rdm.put(DEFINITION_DETAILS_URL, buildDefinitionDetailsUrl(rd)); + rdm.put(DEFINITION_ENABLED, rd.isEnabled()); - // Do the status + // Do the status - end date isn't needed setStatus(rd, details, rdm); + rdm.remove(DEFINITION_ENDED_AT); // Add to the list of finished models models.add(rdm); } } + // Sort the entries + if(sorter != null) { + Collections.sort(models, sorter); + } + // Finish up Map model = new HashMap(); model.put(MODEL_DATA_LIST, models); @@ -100,11 +164,6 @@ public class ReplicationModelBuilder } - protected String buildDefinitionDetailsUrl(ReplicationDefinition replicationDefinition) - { - return "/api/replication-definition/" + replicationDefinition.getReplicationName(); - } - /** * Figures out the status that's one of: * New|Running|CancelRequested|Completed|Failed|Cancelled @@ -138,8 +197,21 @@ public class ReplicationModelBuilder if(details == null) { // It isn't running, we can use the persisted details model.put(DEFINITION_STATUS, replicationDefinition.getExecutionStatus().toString()); - model.put(DEFINITION_STARTED_AT, replicationDefinition.getExecutionStartDate()); - model.put(DEFINITION_ENDED_AT, replicationDefinition.getExecutionEndDate()); + + Date startDate = replicationDefinition.getExecutionStartDate(); + if(startDate != null) { + model.put(DEFINITION_STARTED_AT, ISO8601DateFormat.format(startDate)); + } else { + model.put(DEFINITION_STARTED_AT, null); + } + + Date endDate = replicationDefinition.getExecutionEndDate(); + if(endDate != null) { + model.put(DEFINITION_ENDED_AT, ISO8601DateFormat.format(endDate)); + } else { + model.put(DEFINITION_ENDED_AT, null); + } + return; } @@ -149,7 +221,7 @@ public class ReplicationModelBuilder } else { model.put(DEFINITION_STATUS, "Running"); } - model.put(DEFINITION_STARTED_AT, details.getStartedAt()); + model.put(DEFINITION_STARTED_AT, ISO8601DateFormat.format(details.getStartedAt())); model.put(DEFINITION_ENDED_AT, null); } diff --git a/source/java/org/alfresco/repo/web/scripts/replication/ReplicationRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/replication/ReplicationRestApiTest.java index cff1ea6dba..b24af5dfd8 100644 --- a/source/java/org/alfresco/repo/web/scripts/replication/ReplicationRestApiTest.java +++ b/source/java/org/alfresco/repo/web/scripts/replication/ReplicationRestApiTest.java @@ -22,11 +22,13 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.person.TestPersonManager; import org.alfresco.repo.web.scripts.BaseWebScriptTest; import org.alfresco.service.cmr.action.ActionTrackingService; +import org.alfresco.service.cmr.replication.ReplicationDefinition; import org.alfresco.service.cmr.replication.ReplicationService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.util.GUID; +import org.alfresco.util.ISO8601DateFormat; import org.json.JSONArray; import org.json.JSONObject; import org.springframework.context.ApplicationContext; @@ -74,24 +76,254 @@ public class ReplicationRestApiTest extends BaseWebScriptTest JSONObject json = new JSONObject(jsonStr); JSONArray results = json.getJSONArray("data"); assertNotNull(results); - assertTrue(results.length() == 0); + assertEquals(0, results.length()); // Add a definition, it should show up - // TODO - + ReplicationDefinition rd = replicationService.createReplicationDefinition("Test1", "Testing"); + replicationService.saveReplicationDefinition(rd); + response = sendRequest(new GetRequest(URL_DEFINITIONS), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(1, results.length()); + + JSONObject jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); + // Change the status to running, and re-check - // TODO + actionTrackingService.recordActionExecuting(rd); + String startedAt = ISO8601DateFormat.format(rd.getExecutionStartDate()); + + response = sendRequest(new GetRequest(URL_DEFINITIONS), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(1, results.length()); + + jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("Running", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); // Add a 2nd and 3rd - // TODO + rd = replicationService.createReplicationDefinition("Test2", "2nd Testing"); + replicationService.saveReplicationDefinition(rd); + rd = replicationService.createReplicationDefinition("AnotherTest", "3rd Testing"); + replicationService.saveReplicationDefinition(rd); + + // They should come back sorted by name + response = sendRequest(new GetRequest(URL_DEFINITIONS), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(3, results.length()); + + jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("AnotherTest", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/AnotherTest", jsonRD.get("details")); + + jsonRD = (JSONObject)results.get(1); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("Running", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); + + jsonRD = (JSONObject)results.get(2); + assertNotNull(jsonRD); + assertEquals("Test2", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test2", jsonRD.get("details")); + + + // Sort by status + response = sendRequest(new GetRequest(URL_DEFINITIONS + "?sort=status"), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(3, results.length()); + + // New, name sorts higher + jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("AnotherTest", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/AnotherTest", jsonRD.get("details")); + + // New, name sorts lower + jsonRD = (JSONObject)results.get(1); + assertNotNull(jsonRD); + assertEquals("Test2", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test2", jsonRD.get("details")); + + // Running + jsonRD = (JSONObject)results.get(2); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("Running", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); + + + // Set start times and statuses on these other two + rd = replicationService.loadReplicationDefinition("Test2"); + actionTrackingService.recordActionExecuting(rd); + actionTrackingService.recordActionComplete(rd); + String startedAt2 = ISO8601DateFormat.format(rd.getExecutionStartDate()); + + + // Try the different sorts + response = sendRequest(new GetRequest(URL_DEFINITIONS + "?sort=status"), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(3, results.length()); + + // Complete + jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("Test2", jsonRD.get("name")); + assertEquals("Completed", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt2, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test2", jsonRD.get("details")); + + // New + jsonRD = (JSONObject)results.get(1); + assertNotNull(jsonRD); + assertEquals("AnotherTest", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/AnotherTest", jsonRD.get("details")); + + // Running + jsonRD = (JSONObject)results.get(2); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("Running", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); + + + // By last run + response = sendRequest(new GetRequest(URL_DEFINITIONS + "?sort=lastRun"), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(3, results.length()); + + // Never run first + jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("AnotherTest", jsonRD.get("name")); + assertEquals("New", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(JSONObject.NULL, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/AnotherTest", jsonRD.get("details")); + + // Ran most recently + jsonRD = (JSONObject)results.get(1); + assertNotNull(jsonRD); + assertEquals("Test2", jsonRD.get("name")); + assertEquals("Completed", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt2, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test2", jsonRD.get("details")); + + // Ran least recently + jsonRD = (JSONObject)results.get(2); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("Running", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); // Cancel one of these - // TODO + rd = replicationService.loadReplicationDefinition("AnotherTest"); + rd.setEnabled(false); + replicationService.saveReplicationDefinition(rd); + actionTrackingService.recordActionExecuting(rd); + actionTrackingService.requestActionCancellation(rd); + String startedAt3 = ISO8601DateFormat.format(rd.getExecutionStartDate()); + response = sendRequest(new GetRequest(URL_DEFINITIONS), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + + jsonStr = response.getContentAsString(); + json = new JSONObject(jsonStr); + results = json.getJSONArray("data"); + assertNotNull(results); + assertEquals(3, results.length()); + + jsonRD = (JSONObject)results.get(0); + assertNotNull(jsonRD); + assertEquals("AnotherTest", jsonRD.get("name")); + assertEquals("CancelRequested", jsonRD.get("status")); + assertEquals(false, jsonRD.get("enabled")); + assertEquals(startedAt3, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/AnotherTest", jsonRD.get("details")); + + jsonRD = (JSONObject)results.get(1); + assertNotNull(jsonRD); + assertEquals("Test1", jsonRD.get("name")); + assertEquals("Running", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test1", jsonRD.get("details")); + + jsonRD = (JSONObject)results.get(2); + assertNotNull(jsonRD); + assertEquals("Test2", jsonRD.get("name")); + assertEquals("Completed", jsonRD.get("status")); + assertEquals(true, jsonRD.get("enabled")); + assertEquals(startedAt2, jsonRD.get("startedAt")); + assertEquals("/api/replication-definition/Test2", jsonRD.get("details")); } @Override @@ -109,6 +341,14 @@ public class ReplicationRestApiTest extends BaseWebScriptTest personManager = new TestPersonManager(authenticationService, personService, nodeService); personManager.createPerson(USER_NORMAL); + + // Ensure we start with no replication definitions + // (eg another test left them behind) + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + for(ReplicationDefinition rd : replicationService.loadReplicationDefinitions()) { + replicationService.deleteReplicationDefinition(rd); + } + AuthenticationUtil.clearCurrentSecurityContext(); } /* (non-Javadoc) @@ -118,6 +358,14 @@ public class ReplicationRestApiTest extends BaseWebScriptTest protected void tearDown() throws Exception { super.tearDown(); + personManager.clearPeople(); + + // Zap any replication definitions we created + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + for(ReplicationDefinition rd : replicationService.loadReplicationDefinitions()) { + replicationService.deleteReplicationDefinition(rd); + } + AuthenticationUtil.clearCurrentSecurityContext(); } }