[feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details

This commit is contained in:
mohit-singh4
2024-09-13 00:50:37 +05:30
parent 1e42e13f7f
commit 47799cdbdd
9 changed files with 232 additions and 272 deletions

View File

@@ -29,11 +29,12 @@ import org.alfresco.rest.api.model.NodeSizeDetails;
public interface SizeDetails public interface SizeDetails
{ {
enum PROCESSINGSTATE
{
NOT_INITIATED, IN_PROGRESS, COMPLETED;
}
NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId); NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId);
NodeSizeDetails getNodeSizeDetails(String nodeId, String jobId); NodeSizeDetails getNodeSizeDetails(String nodeId, String jobId);
enum ProcessingState
{
NOT_INITIATED, PENDING, IN_PROGRESS, COMPLETED
}
} }

View File

@@ -25,44 +25,32 @@
*/ */
package org.alfresco.rest.api.impl; package org.alfresco.rest.api.impl;
import static org.alfresco.rest.api.SizeDetails.ProcessingState.COMPLETED;
import static org.alfresco.rest.api.SizeDetails.ProcessingState.IN_PROGRESS;
import static org.alfresco.rest.api.SizeDetails.ProcessingState.NOT_INITIATED;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.NodeSizeDetailActionExecutor; import org.alfresco.repo.action.executer.NodeSizeDetailActionExecutor;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.SizeDetails; import org.alfresco.rest.api.SizeDetails;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.NodePermissions;
import org.alfresco.rest.api.model.NodeSizeDetails; import org.alfresco.rest.api.model.NodeSizeDetails;
import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException; import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException;
import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException; import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import java.io.Serializable;
import java.util.Map;
import java.util.HashMap;
import static org.alfresco.rest.api.SizeDetails.PROCESSINGSTATE.COMPLETED;
import static org.alfresco.rest.api.SizeDetails.PROCESSINGSTATE.NOT_INITIATED;
import static org.alfresco.rest.api.SizeDetails.PROCESSINGSTATE.IN_PROGRESS;
public class SizeDetailsImpl implements SizeDetails public class SizeDetailsImpl implements SizeDetails
{ {
private static final Logger LOG = LoggerFactory.getLogger(SizeDetailsImpl.class); private static final String ACTION_ID = "actionId";
private static final String STATUS = "status";
private static final String ACTIONID = "actionId";
private static final String INVALID_NODEID = "Invalid parameter: value of nodeId is invalid";
private static final String INVALID_JOBID = "Invalid parameter: value of jobId is invalid";
private static final String FOLDER = "folder";
private Nodes nodes; private Nodes nodes;
private NodeService nodeService; private NodeRef nodeRef;
private PermissionService permissionService;
private ActionService actionService; private ActionService actionService;
private SimpleCache<Serializable, Map<String, Object>> simpleCache; private SimpleCache<Serializable, Map<String, Object>> simpleCache;
private int defaultItems; private int defaultItems;
@@ -72,16 +60,6 @@ public class SizeDetailsImpl implements SizeDetails
this.nodes = nodes; this.nodes = nodes;
} }
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setActionService(ActionService actionService) public void setActionService(ActionService actionService)
{ {
this.actionService = actionService; this.actionService = actionService;
@@ -103,16 +81,17 @@ public class SizeDetailsImpl implements SizeDetails
@Override @Override
public NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId) public NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId)
{ {
NodeRef nodeRef = nodes.validateNode(nodeId); nodeRef = nodes.validateOrLookupNode(nodeId);
validateType(nodeRef); validateType(nodeRef);
String actionId; String actionId;
if(simpleCache.get(nodeId) == null) if (!simpleCache.contains(nodeId))
{ {
actionId = executeAction(nodeRef, defaultItems, simpleCache); actionId = executeAction();
} else }
else
{ {
Map<String, Object> result = simpleCache.get(nodeRef.getId()); Map<String, Object> result = simpleCache.get(nodeRef.getId());
actionId = (String)result.get(ACTIONID); actionId = (String) result.get(ACTION_ID);
} }
return new NodeSizeDetails(actionId); return new NodeSizeDetails(actionId);
} }
@@ -123,29 +102,27 @@ public class SizeDetailsImpl implements SizeDetails
@Override @Override
public NodeSizeDetails getNodeSizeDetails(final String nodeId, final String jobId) public NodeSizeDetails getNodeSizeDetails(final String nodeId, final String jobId)
{ {
NodeRef nodeRef = nodes.validateNode(nodeId); NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
validateType(nodeRef); validateType(nodeRef);
if(simpleCache.get(nodeId) == null) if (!simpleCache.contains(nodeId))
{ {
return new NodeSizeDetails(nodeId, NOT_INITIATED.name()); return new NodeSizeDetails(nodeId, NOT_INITIATED.name());
} }
LOG.debug("Executing executorResultToSizeDetail method");
return executorResultToSizeDetail(simpleCache.get(nodeId), nodeId, jobId); return executorResultToSizeDetail(simpleCache.get(nodeId), nodeId, jobId);
} }
/** /**
* Executing Action Asynchronously. * Executing Action Asynchronously.
*/ */
private String executeAction(NodeRef nodeRef, int defaultItems, SimpleCache<Serializable, Map<String, Object>> simpleCache) private String executeAction()
{ {
Map<String, Object > currentStatus = new HashMap<>(); Map<String, Object > currentStatus = new HashMap<>();
currentStatus.put(STATUS,IN_PROGRESS.name()); currentStatus.put("status",IN_PROGRESS.name());
Action folderSizeAction = actionService.createAction(NodeSizeDetailActionExecutor.NAME); Action folderSizeAction = actionService.createAction(NodeSizeDetailActionExecutor.NAME);
currentStatus.put(ACTIONID,folderSizeAction.getId()); currentStatus.put(ACTION_ID,folderSizeAction.getId());
folderSizeAction.setTrackStatus(true); folderSizeAction.setTrackStatus(true);
folderSizeAction.setExecuteAsynchronously(true);
folderSizeAction.setParameterValue(NodeSizeDetailActionExecutor.DEFAULT_SIZE, defaultItems); folderSizeAction.setParameterValue(NodeSizeDetailActionExecutor.DEFAULT_SIZE, defaultItems);
simpleCache.put(nodeRef.getId(),currentStatus); simpleCache.put(nodeRef.getId(),currentStatus);
actionService.executeAction(folderSizeAction, nodeRef, false, true); actionService.executeAction(folderSizeAction, nodeRef, false, true);
@@ -162,21 +139,17 @@ public class SizeDetailsImpl implements SizeDetails
return new NodeSizeDetails(nodeId, COMPLETED.name()); return new NodeSizeDetails(nodeId, COMPLETED.name());
} }
// Check for the presence of "size" key. if (result.containsKey("size"))
boolean hasSizeKey = result.containsKey("size");
if (hasSizeKey)
{ {
NodeSizeDetails nodeSizeDetails = new NodeSizeDetails((String) result.get("nodeId"), NodeSizeDetails nodeSizeDetails =
(Long) result.get("size"), new NodeSizeDetails((String) result.get("nodeId"), (Long) result.get("size"),
(String) result.get("calculatedAt"), (Date) result.get("calculatedAt"), (Integer) result.get("numberOfFiles"),
(Integer) result.get("numberOfFiles"), COMPLETED.name(), (String) result.get(ACTION_ID));
COMPLETED.name(),
(String) result.get(ACTIONID));
if(!nodeSizeDetails.getJobId().equalsIgnoreCase(jobId)) if (!nodeSizeDetails.getJobId()
.equalsIgnoreCase(jobId))
{ {
throw new UnprocessableContentException(INVALID_JOBID); throw new UnprocessableContentException("Invalid parameter: value of jobId is invalid");
} }
return nodeSizeDetails; return nodeSizeDetails;
} }
@@ -188,25 +161,9 @@ public class SizeDetailsImpl implements SizeDetails
private void validateType(NodeRef nodeRef) throws InvalidNodeTypeException private void validateType(NodeRef nodeRef) throws InvalidNodeTypeException
{ {
QName qName = nodeService.getType(nodeRef); if (!nodes.isSubClass(nodeRef, ContentModel.TYPE_FOLDER, false))
validatePermissions(nodeRef, nodeRef.getId());
if(!FOLDER.equalsIgnoreCase(qName.getLocalName()))
{ {
throw new InvalidNodeTypeException(INVALID_NODEID); throw new InvalidNodeTypeException("Invalid parameter: value of nodeId is invalid");
}
}
/**
* Validating node permission [i.e. READ permission should be there ]
*/
private void validatePermissions(NodeRef nodeRef, String nodeId)
{
Node nodeInfo = nodes.getNode(nodeId);
NodePermissions nodePerms = nodeInfo.getPermissions();
// Validate permissions.
if (nodePerms != null && permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED)
{
throw new AccessDeniedException("permissions.err_access_denied");
} }
} }
} }

View File

@@ -25,17 +25,17 @@
*/ */
package org.alfresco.rest.api.model; package org.alfresco.rest.api.model;
import org.json.simple.JSONObject; import java.util.Date;
import java.util.Objects; import java.util.Objects;
import org.json.simple.JSONObject;
public class NodeSizeDetails public class NodeSizeDetails
{ {
private String nodeId; private String id;
private Long size; private Long sizeInBytes;
private String calculatedAt; private Date calculatedAt;
private Integer numberOfFiles; private Integer numberOfFiles;
private String status;
private String jobId; private String jobId;
public NodeSizeDetails(String jobId) public NodeSizeDetails(String jobId)
@@ -43,48 +43,62 @@ public class NodeSizeDetails
this.jobId = jobId; this.jobId = jobId;
} }
public NodeSizeDetails(String nodeId, String status) public NodeSizeDetails(String id, String currentStatus)
{ {
this.nodeId = nodeId; this.id = id;
this.status = status; NodeSizeDetails.status.valueOf(currentStatus);
} }
public NodeSizeDetails(String nodeId, Long size, String calculatedAt, Integer numberOfFiles, String status, String jobId) public NodeSizeDetails(String id, Long sizeInBytes, Date calculatedAt, Integer numberOfFiles, String currentStatus,
String jobId)
{ {
this.nodeId = nodeId; this.id = id;
this.size = size; this.sizeInBytes = sizeInBytes;
this.calculatedAt = calculatedAt; this.calculatedAt = calculatedAt;
this.numberOfFiles = numberOfFiles; this.numberOfFiles = numberOfFiles;
this.status = status; NodeSizeDetails.status.valueOf(currentStatus);
this.jobId = jobId; this.jobId = jobId;
} }
public String getNodeId() public static String parseJson(JSONObject jsonObject)
{ {
return nodeId; if (jsonObject == null)
{
return null;
} }
public void setNodeId(String nodeId) String jobId = (String) jsonObject.get("jobId");
{ jobId = jobId.replace("<", "")
this.nodeId = nodeId; .replace(">", "");
return jobId;
} }
public Long getSize() public String getId()
{ {
return size; return id;
} }
public void setSize(Long size) public void setId(String id)
{ {
this.size = size; this.id = id;
} }
public String getCalculatedAt() public Long getSizeInBytes()
{
return sizeInBytes;
}
public void setSizeInBytes(Long sizeInBytes)
{
this.sizeInBytes = sizeInBytes;
}
public Date getCalculatedAt()
{ {
return calculatedAt; return calculatedAt;
} }
public void setCalculatedAt(String calculatedAt) public void setCalculatedAt(Date calculatedAt)
{ {
this.calculatedAt = calculatedAt; this.calculatedAt = calculatedAt;
} }
@@ -99,16 +113,6 @@ public class NodeSizeDetails
this.numberOfFiles = numberOfFiles; this.numberOfFiles = numberOfFiles;
} }
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public String getJobId() public String getJobId()
{ {
return jobId; return jobId;
@@ -119,18 +123,6 @@ public class NodeSizeDetails
this.jobId = jobId; this.jobId = jobId;
} }
public static String parseJson(JSONObject jsonObject)
{
if (jsonObject == null)
{
return null;
}
String jobId = (String)jsonObject.get("jobId");
jobId = jobId.replace("<", "").replace(">", "");
return jobId;
}
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {
@@ -143,25 +135,27 @@ public class NodeSizeDetails
return false; return false;
} }
NodeSizeDetails that = (NodeSizeDetails) o; NodeSizeDetails that = (NodeSizeDetails) o;
return Objects.equals(nodeId, that.nodeId) && Objects.equals(size, that.size) && Objects.equals(calculatedAt, that.calculatedAt) && Objects.equals(numberOfFiles, that.numberOfFiles) && Objects.equals(status, that.status) && Objects.equals(jobId, that.jobId); return Objects.equals(id, that.id) && Objects.equals(sizeInBytes, that.sizeInBytes) && Objects.equals(
calculatedAt, that.calculatedAt) && Objects.equals(numberOfFiles, that.numberOfFiles)
&& Objects.equals(jobId, that.jobId);
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
return Objects.hash(nodeId, size, calculatedAt, numberOfFiles, status, jobId); return Objects.hash(id, sizeInBytes, calculatedAt, numberOfFiles, jobId);
} }
@Override @Override
public String toString() public String toString()
{ {
return "NodeSizeDetails{" + return "NodeSizeDetails{" + "id='" + id + '\'' + ", sizeInBytes=" + sizeInBytes + ", calculatedAt="
"nodeId='" + nodeId + '\'' + + calculatedAt + ", numberOfFiles=" + numberOfFiles + ", jobId='" + jobId + '\'' + '}';
", size=" + size +
", calculatedAt='" + calculatedAt + '\'' +
", numberOfFiles=" + numberOfFiles +
", status='" + status + '\'' +
", jobId='" + jobId + '\'' +
'}';
} }
private enum status
{
NOT_INITIATED, PENDING, IN_PROGRESS, COMPLETED
}
} }

View File

@@ -26,6 +26,9 @@
package org.alfresco.rest.api.nodes; package org.alfresco.rest.api.nodes;
import java.util.Collections;
import java.util.List;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.SizeDetails; import org.alfresco.rest.api.SizeDetails;
import org.alfresco.rest.api.model.NodeSizeDetails; import org.alfresco.rest.api.model.NodeSizeDetails;
@@ -38,20 +41,14 @@ import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.util.ParameterCheck; import org.alfresco.util.ParameterCheck;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import java.util.Arrays;
import java.util.List;
@RelationshipResource(name = "size-details", entityResource = NodesEntityResource.class, title = "Node Size Details") @RelationshipResource(name = "size-details", entityResource = NodesEntityResource.class, title = "Node Size Details")
public class NodeSizeDetailsRelation implements public class NodeSizeDetailsRelation implements RelationshipResourceAction.ReadById<NodeSizeDetails>,
RelationshipResourceAction.ReadById<NodeSizeDetails>, RelationshipResourceAction.Create<NodeSizeDetails>, InitializingBean
RelationshipResourceAction.Create<NodeSizeDetails>, {
InitializingBean {
private static final Logger LOG = LoggerFactory.getLogger(NodeSizeDetailsRelation.class);
private Nodes nodes; private Nodes nodes;
private SizeDetails sizeDetails; private SizeDetails sizeDetails;
@@ -68,27 +65,27 @@ public class NodeSizeDetailsRelation implements
@Override @Override
public void afterPropertiesSet() public void afterPropertiesSet()
{ {
ParameterCheck.mandatory("nodes", this.nodes); ParameterCheck.mandatory("sizeDetails", this.sizeDetails);
} }
@WebApiDescription(title = "Create node-size details request", successStatus = Status.STATUS_ACCEPTED) @WebApiDescription(title = "Create node-size details request", successStatus = Status.STATUS_ACCEPTED)
@WebApiParam(name="nodeSizeEntity", title="Node Size Details Request", description="Request for processing Node Size.", @WebApiParam(name = "nodeSizeEntity", title = "Node Size Details Request",
kind= ResourceParameter.KIND.HTTP_BODY_OBJECT, allowMultiple=false) description = "Request for processing Node Size.", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT,
allowMultiple = false)
@Override @Override
public List<NodeSizeDetails> create(String nodeId, List<NodeSizeDetails> nodeSizeEntity, Parameters parameters) public List<NodeSizeDetails> create(String nodeId, List<NodeSizeDetails> nodeSizeEntity, Parameters parameters)
{ {
LOG.debug(" Executing generateNodeSizeDetailsRequest method "); return List.of(sizeDetails.generateNodeSizeDetailsRequest(nodeId));
return Arrays.asList(sizeDetails.generateNodeSizeDetailsRequest(nodeId));
} }
@WebApiDescription(title = "Get Node Size Details", description = "Get the Node Size Details") @WebApiDescription(title = "Get Node Size Details", description = "Get the Node Size Details")
@WebApiParameters({ @WebApiParameters({ @WebApiParam(name = "nodeId", title = "The unique id of the Node being addressed",
@WebApiParam(name="nodeId", title="The unique id of the Node being addressed", description="A single node id"), description = "A single node id"),
@WebApiParam(name = "jobId", title = "Job Id to get the NodeSizeDetails", description = "JobId") }) @WebApiParam(name = "jobId", title = "Job Id to get the NodeSizeDetails", description = "JobId") })
@Override @Override
public NodeSizeDetails readById(String nodeId, String jobId, Parameters parameters) throws RelationshipResourceNotFoundException public NodeSizeDetails readById(String nodeId, String jobId, Parameters parameters)
throws RelationshipResourceNotFoundException
{ {
LOG.debug(" Executing getNodeSizeDetails method ");
return sizeDetails.getNodeSizeDetails(nodeId, jobId); return sizeDetails.getNodeSizeDetails(nodeId, jobId);
} }
} }

View File

@@ -998,8 +998,6 @@
<bean id="sizeDetailsImpl" class="org.alfresco.rest.api.impl.SizeDetailsImpl"> <bean id="sizeDetailsImpl" class="org.alfresco.rest.api.impl.SizeDetailsImpl">
<property name="nodes" ref="nodes" /> <property name="nodes" ref="nodes" />
<property name="nodeService" ref="NodeService" />
<property name="permissionService" ref="permissionService"/>
<property name="actionService" ref="ActionService"/> <property name="actionService" ref="ActionService"/>
<property name="simpleCache" ref="folderSizeSharedCache" /> <property name="simpleCache" ref="folderSizeSharedCache" />
<property name="defaultItems" value="${alfresco.restApi.calculateFolderSize.items}"/> <property name="defaultItems" value="${alfresco.restApi.calculateFolderSize.items}"/>

View File

@@ -25,6 +25,13 @@
*/ */
package org.alfresco.rest.api.impl; package org.alfresco.rest.api.impl;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.repo.action.executer.NodeSizeDetailActionExecutor; import org.alfresco.repo.action.executer.NodeSizeDetailActionExecutor;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
@@ -39,27 +46,19 @@ import org.alfresco.service.namespace.QName;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.Serializable;
import java.util.Map;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/** /**
* Unit tests for {@link SizeDetailsImpl} class. * Unit tests for {@link SizeDetailsImpl} class.
*
*/ */
public class SizeDetailsImplTest public class SizeDetailsImplTest
{ {
private final static int DEFAULT_ITEMS = 1000; private final static int DEFAULT_ITEMS = 1000;
private static final String NAMESPACE = "http://www.alfresco.org/test/NodeSizeDetailsTest";
private static final QName TYPE_FOLDER = QName.createQName(NAMESPACE, "folder");
private SizeDetailsImpl sizeDetailsImpl; private SizeDetailsImpl sizeDetailsImpl;
private Nodes nodes; private Nodes nodes;
private NodeService nodeService; private NodeService nodeService;
private ActionService actionService; private ActionService actionService;
private Action action; private Action action;
private static final String NAMESPACE = "http://www.alfresco.org/test/NodeSizeDetailsTest";
private static final QName TYPE_FOLDER = QName.createQName(NAMESPACE, "folder");
@Before @Before
public void setUp() public void setUp()

View File

@@ -25,12 +25,24 @@
*/ */
package org.alfresco.rest.api.tests; package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.NodeSizeDetails; import org.alfresco.rest.api.model.NodeSizeDetails;
import org.alfresco.rest.api.model.Site; import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient; import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.data.*; import org.alfresco.rest.api.tests.client.data.ContentInfo;
import org.alfresco.rest.api.tests.client.data.Document;
import org.alfresco.rest.api.tests.client.data.Node;
import org.alfresco.rest.api.tests.client.data.UserInfo;
import org.alfresco.rest.api.tests.util.RestApiUtil; import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.site.SiteVisibility;
@@ -45,15 +57,6 @@ import org.junit.runners.MethodSorters;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
/** /**
* V1 REST API tests for calculating and retrieving Folder size. * V1 REST API tests for calculating and retrieving Folder size.
*/ */
@@ -123,9 +126,11 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
// Perform POST request // Perform POST request
HttpResponse postResponse = post(generateNodeSizeDetailsUrl(folderId), null, 202); HttpResponse postResponse = post(generateNodeSizeDetailsUrl(folderId), null, 202);
assertNotNull("After executing POST/size-details first time, it will provide jobId with 202 status code",postResponse.getJsonResponse()); assertNotNull("After executing POST/size-details first time, it will provide jobId with 202 status code",
postResponse.getJsonResponse());
String jobId = NodeSizeDetails.parseJson((JSONObject)postResponse.getJsonResponse().get("entry")); String jobId = NodeSizeDetails.parseJson((JSONObject) postResponse.getJsonResponse()
.get("entry"));
assertNotNull("In response, JobId should be present", jobId); assertNotNull("In response, JobId should be present", jobId);
@@ -136,11 +141,13 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
HttpResponse getResponse = getSingle(getNodeSizeDetailsUrl(folderId, jobId), null, 200); HttpResponse getResponse = getSingle(getNodeSizeDetailsUrl(folderId, jobId), null, 200);
assertNotNull("After executing GET/size-details, it will provide NodeSizeDetails with 200 status code",getResponse.getJsonResponse()); assertNotNull("After executing GET/size-details, it will provide NodeSizeDetails with 200 status code",
getResponse.getJsonResponse());
String getJsonResponse = String.valueOf(getResponse.getJsonResponse()); String getJsonResponse = String.valueOf(getResponse.getJsonResponse());
assertTrue("We are not getting correct response "+getJsonResponse,getJsonResponse.contains("size") || getJsonResponse.contains("status")); assertTrue("We are not getting correct response " + getJsonResponse,
getJsonResponse.contains("size") || getJsonResponse.contains("status"));
} }
@Test @Test
@@ -173,14 +180,17 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
assertEquals(500, nodes.size()); assertEquals(500, nodes.size());
//Start Time before triggering POST/size-details API //Start Time before triggering POST/size-details API
LocalTime expectedTime = LocalTime.now().plusSeconds(5); LocalTime expectedTime = LocalTime.now()
.plusSeconds(5);
// Perform POST request // Perform POST request
HttpResponse postResponse = post(generateNodeSizeDetailsUrl(parentFolder), null, 202); HttpResponse postResponse = post(generateNodeSizeDetailsUrl(parentFolder), null, 202);
assertNotNull("After executing POST/size-details first time, it will provide jobId with 202 status code",postResponse.getJsonResponse()); assertNotNull("After executing POST/size-details first time, it will provide jobId with 202 status code",
postResponse.getJsonResponse());
String jobId = NodeSizeDetails.parseJson((JSONObject)postResponse.getJsonResponse().get("entry")); String jobId = NodeSizeDetails.parseJson((JSONObject) postResponse.getJsonResponse()
.get("entry"));
assertNotNull("In response, JobId should be present", jobId); assertNotNull("In response, JobId should be present", jobId);
@@ -191,11 +201,13 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
HttpResponse getResponse = getSingle(getNodeSizeDetailsUrl(folderId, jobId), null, 200); HttpResponse getResponse = getSingle(getNodeSizeDetailsUrl(folderId, jobId), null, 200);
assertNotNull("After executing GET/size-details, it will provide NodeSizeDetails with 200 status code",getResponse.getJsonResponse()); assertNotNull("After executing GET/size-details, it will provide NodeSizeDetails with 200 status code",
getResponse.getJsonResponse());
String getJsonResponse = String.valueOf(getResponse.getJsonResponse()); String getJsonResponse = String.valueOf(getResponse.getJsonResponse());
assertTrue("We are not getting correct response "+getJsonResponse,getJsonResponse.contains("size") || getJsonResponse.contains("status")); assertTrue("We are not getting correct response " + getJsonResponse,
getJsonResponse.contains("size") || getJsonResponse.contains("status"));
//current Time after executing GET/size-details //current Time after executing GET/size-details
LocalTime actualTime = LocalTime.now(); LocalTime actualTime = LocalTime.now();

View File

@@ -25,6 +25,17 @@
*/ */
package org.alfresco.repo.action.executer; package org.alfresco.repo.action.executer;
import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.action.ParameterizedItemAbstractBase; import org.alfresco.repo.action.ParameterizedItemAbstractBase;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
@@ -34,20 +45,11 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.util.ISO8601DateFormat;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* NodeSizeDetailActionExecutor * NodeSizeDetailActionExecutor
* Executing Alfresco FTS Query to find size of Folder Node * Executing Alfresco FTS Query to find size of Folder Node
@@ -63,7 +65,11 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
*/ */
public static final String NAME = "folder-size"; public static final String NAME = "folder-size";
public static final String EXCEPTION = "Exception"; public static final String EXCEPTION = "Exception";
public static final String IN_PROGRESS = "IN_PROGRESS";
public static final String DEFAULT_SIZE = "default-size"; public static final String DEFAULT_SIZE = "default-size";
private static final String STATUS = "status";
private static final String ACTION_ID = "actionId";
private static final String FIELD_FACET = "content.size"; private static final String FIELD_FACET = "content.size";
private static final String FACET_QUERY = "content.size:[0 TO " + Integer.MAX_VALUE + "] \"label\": \"large\",\"group\":\"Size\""; private static final String FACET_QUERY = "content.size:[0 TO " + Integer.MAX_VALUE + "] \"label\": \"large\",\"group\":\"Size\"";
private SearchService searchService; private SearchService searchService;
@@ -98,6 +104,9 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
Serializable serializable = nodeAction.getParameterValue(DEFAULT_SIZE); Serializable serializable = nodeAction.getParameterValue(DEFAULT_SIZE);
int defaultItems; int defaultItems;
Map<String, Object> response = new HashMap<>(); Map<String, Object> response = new HashMap<>();
response.put(STATUS, IN_PROGRESS);
response.put(ACTION_ID, nodeAction.getId());
simpleCache.put(actionedUponNodeRef.getId(), response);
try try
{ {
@@ -106,7 +115,8 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
catch (NumberFormatException numberFormatException) catch (NumberFormatException numberFormatException)
{ {
LOG.error("Exception occurred while parsing String to INT: {} ", numberFormatException.getMessage()); LOG.error("Exception occurred while parsing String to INT: {} ", numberFormatException.getMessage());
response.put(EXCEPTION,"Exception occurred while parsing String to INT: {} " + numberFormatException.getMessage()); response.put(EXCEPTION,
"Exception occurred while parsing String to INT: {} " + numberFormatException.getMessage());
simpleCache.put(actionedUponNodeRef.getId(), response); simpleCache.put(actionedUponNodeRef.getId(), response);
throw numberFormatException; throw numberFormatException;
} }
@@ -122,38 +132,30 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
{ {
// executing Alfresco FTS facet query. // executing Alfresco FTS facet query.
results = facetQuery(nodeRef); results = facetQuery(nodeRef);
totalItems = Math.min(results.getFieldFacet(FIELD_FACET).size(), defaultItems); totalItems = Math.min(results.getFieldFacet(FIELD_FACET)
// Using AtomicLong to accumulate the total size. .size(), defaultItems);
AtomicLong resultSize = new AtomicLong(0);
while (!isCalculationCompleted) while (!isCalculationCompleted)
{ {
List<Pair<String, Integer>> pairSizes = results.getFieldFacet(FIELD_FACET).subList(skipCount, totalItems); List<Pair<String, Integer>> pairSizes = results.getFieldFacet(FIELD_FACET)
pairSizes.parallelStream().forEach(id -> { .subList(skipCount, totalItems);
try long total = pairSizes.parallelStream()
{ .mapToLong(id -> Long.parseLong(id.getFirst()) * id.getSecond())
if(id.getSecond()>0) .sum();
{
resultSize.addAndGet(Long.valueOf(id.getFirst()) * id.getSecond());
}
}
catch (Exception e)
{
resultSize.addAndGet(0);
}
});
totalSizeFromFacet+=resultSize.longValue(); totalSizeFromFacet += total;
resultSize.set(0);
if (results.getFieldFacet(FIELD_FACET).size() <= totalItems || results.getFieldFacet(FIELD_FACET).size() <= defaultItems) if (results.getFieldFacet(FIELD_FACET)
.size() <= totalItems || results.getFieldFacet(FIELD_FACET)
.size() <= defaultItems)
{ {
isCalculationCompleted = true; isCalculationCompleted = true;
} }
else else
{ {
skipCount += defaultItems; skipCount += defaultItems;
int remainingItems = results.getFieldFacet(FIELD_FACET).size() - totalItems; int remainingItems = results.getFieldFacet(FIELD_FACET)
.size() - totalItems;
totalItems += Math.min(remainingItems, defaultItems); totalItems += Math.min(remainingItems, defaultItems);
} }
} }
@@ -161,7 +163,8 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
catch (RuntimeException runtimeException) catch (RuntimeException runtimeException)
{ {
LOG.error("Exception occurred in NodeSizeDetailActionExecutor:results {} ", runtimeException.getMessage()); LOG.error("Exception occurred in NodeSizeDetailActionExecutor:results {} ", runtimeException.getMessage());
response.put(EXCEPTION,"Exception occurred in NodeSizeDetailActionExecutor:results {} "+runtimeException.getMessage()); response.put(EXCEPTION, "Exception occurred in NodeSizeDetailActionExecutor:results {} "
+ runtimeException.getMessage());
simpleCache.put(nodeRef.getId(), response); simpleCache.put(nodeRef.getId(), response);
throw runtimeException; throw runtimeException;
} }
@@ -170,10 +173,16 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
final LocalDateTime eventTimestamp = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); final LocalDateTime eventTimestamp = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss");
String formattedTimestamp = eventTimestamp.format(formatter); String formattedTimestamp = eventTimestamp.format(formatter);
Date date = Calendar.getInstance()
.getTime();
String dateStr = ISO8601DateFormat.format(date);
response.put("nodeId", nodeRef.getId()); response.put("nodeId", nodeRef.getId());
response.put("size", totalSizeFromFacet); response.put("size", totalSizeFromFacet);
response.put("calculatedAt", formattedTimestamp); response.put("calculatedAt", dateStr);
response.put("numberOfFiles", results != null ? results.getNodeRefs().size() : 0); response.put("numberOfFiles", results != null ? results.getNodeRefs()
.size() : 0);
response.put("actionId", nodeAction.getId()); response.put("actionId", nodeAction.getId());
if (isCalculationCompleted) if (isCalculationCompleted)
@@ -184,9 +193,7 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
protected ResultSet facetQuery(NodeRef nodeRef) protected ResultSet facetQuery(NodeRef nodeRef)
{ {
StringBuilder aftsQuery = new StringBuilder(); String query = "ANCESTOR:\"" + nodeRef + "\" AND TYPE:content";
aftsQuery.append("ANCESTOR:\"").append(nodeRef).append("\" AND TYPE:content");
String query = aftsQuery.toString();
SearchParameters searchParameters = new SearchParameters(); SearchParameters searchParameters = new SearchParameters();
searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
@@ -196,7 +203,8 @@ public class NodeSizeDetailActionExecutor extends ActionExecuterAbstractBase
searchParameters.addFacetQuery(FACET_QUERY); searchParameters.addFacetQuery(FACET_QUERY);
final SearchParameters.FieldFacet ff = new SearchParameters.FieldFacet(FIELD_FACET); final SearchParameters.FieldFacet ff = new SearchParameters.FieldFacet(FIELD_FACET);
ff.setLimitOrNull(resultsWithoutFacet.getNodeRefs().size()); ff.setLimitOrNull(resultsWithoutFacet.getNodeRefs()
.size());
searchParameters.addFieldFacet(ff); searchParameters.addFieldFacet(ff);
resultsWithoutFacet.close(); resultsWithoutFacet.close();
return searchService.query(searchParameters); return searchService.query(searchParameters);

View File

@@ -45,21 +45,18 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional @Transactional
public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
{ {
/**
* Id used to identify the test action created
*/
private final static String ID = GUID.generate();
/** /**
* The test node reference * The test node reference
*/ */
private NodeRef nodeRef; private NodeRef nodeRef;
/** /**
* The folder Size executer * The folder Size executer
*/ */
private NodeSizeDetailActionExecutor executer; private NodeSizeDetailActionExecutor executer;
/**
* Id used to identify the test action created
*/
private final static String ID = GUID.generate();
/** /**
* Set the simpleCache service * Set the simpleCache service
* *
@@ -77,20 +74,17 @@ public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
StoreRef testStoreRef; StoreRef testStoreRef;
NodeRef rootNodeRef; NodeRef rootNodeRef;
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent"); AuthenticationComponent authenticationComponent =
(AuthenticationComponent) applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
// Create the store and get the root node // Create the store and get the root node
testStoreRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" testStoreRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
+ System.currentTimeMillis());
rootNodeRef = nodeService.getRootNode(testStoreRef); rootNodeRef = nodeService.getRootNode(testStoreRef);
// Create the node used for tests // Create the node used for tests
this.nodeRef = nodeService.createNode( this.nodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN,
rootNodeRef, QName.createQName("{test}testnode"), ContentModel.TYPE_CONTENT).getChildRef();
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"),
ContentModel.TYPE_CONTENT).getChildRef();
// Get the executer instance. // Get the executer instance.
this.executer = (NodeSizeDetailActionExecutor) this.applicationContext.getBean(NodeSizeDetailActionExecutor.NAME); this.executer = (NodeSizeDetailActionExecutor) this.applicationContext.getBean(NodeSizeDetailActionExecutor.NAME);
@@ -110,6 +104,6 @@ public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
this.executer.executeImpl(action, this.nodeRef); this.executer.executeImpl(action, this.nodeRef);
Object resultAction = simpleCache.get(this.nodeRef.getId()); Object resultAction = simpleCache.get(this.nodeRef.getId());
Map<String, Object> mapResult = (Map<String, Object>) resultAction; Map<String, Object> mapResult = (Map<String, Object>) resultAction;
assertTrue(mapResult != null); assertNotNull(mapResult);
} }
} }