[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
{
enum PROCESSINGSTATE
{
NOT_INITIATED, IN_PROGRESS, COMPLETED;
}
NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId);
NodeSizeDetails getNodeSizeDetails(String nodeId, String jobId);
enum ProcessingState
{
NOT_INITIATED, PENDING, IN_PROGRESS, COMPLETED
}
}

View File

@@ -25,46 +25,34 @@
*/
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.cache.SimpleCache;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.rest.api.Nodes;
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.framework.core.exceptions.InvalidNodeTypeException;
import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException;
import org.alfresco.service.cmr.action.Action;
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 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
{
private static final Logger LOG = LoggerFactory.getLogger(SizeDetailsImpl.class);
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 static final String ACTION_ID = "actionId";
private Nodes nodes;
private NodeService nodeService;
private PermissionService permissionService;
private NodeRef nodeRef;
private ActionService actionService;
private SimpleCache<Serializable,Map<String, Object>> simpleCache;
private SimpleCache<Serializable, Map<String, Object>> simpleCache;
private int defaultItems;
public void setNodes(Nodes nodes)
@@ -72,16 +60,6 @@ public class SizeDetailsImpl implements SizeDetails
this.nodes = nodes;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
@@ -103,16 +81,17 @@ public class SizeDetailsImpl implements SizeDetails
@Override
public NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
nodeRef = nodes.validateOrLookupNode(nodeId);
validateType(nodeRef);
String actionId;
if(simpleCache.get(nodeId) == null)
if (!simpleCache.contains(nodeId))
{
actionId = executeAction(nodeRef, defaultItems, simpleCache);
} else
actionId = executeAction();
}
else
{
Map<String, Object> result = simpleCache.get(nodeRef.getId());
actionId = (String)result.get(ACTIONID);
actionId = (String) result.get(ACTION_ID);
}
return new NodeSizeDetails(actionId);
}
@@ -123,29 +102,27 @@ public class SizeDetailsImpl implements SizeDetails
@Override
public NodeSizeDetails getNodeSizeDetails(final String nodeId, final String jobId)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
validateType(nodeRef);
if(simpleCache.get(nodeId) == null)
if (!simpleCache.contains(nodeId))
{
return new NodeSizeDetails(nodeId, NOT_INITIATED.name());
}
LOG.debug("Executing executorResultToSizeDetail method");
return executorResultToSizeDetail(simpleCache.get(nodeId), nodeId, jobId);
}
/**
* 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<>();
currentStatus.put(STATUS,IN_PROGRESS.name());
currentStatus.put("status",IN_PROGRESS.name());
Action folderSizeAction = actionService.createAction(NodeSizeDetailActionExecutor.NAME);
currentStatus.put(ACTIONID,folderSizeAction.getId());
currentStatus.put(ACTION_ID,folderSizeAction.getId());
folderSizeAction.setTrackStatus(true);
folderSizeAction.setExecuteAsynchronously(true);
folderSizeAction.setParameterValue(NodeSizeDetailActionExecutor.DEFAULT_SIZE, defaultItems);
simpleCache.put(nodeRef.getId(),currentStatus);
actionService.executeAction(folderSizeAction, nodeRef, false, true);
@@ -155,28 +132,24 @@ public class SizeDetailsImpl implements SizeDetails
/**
* Converting action executor response to their respective model class.
*/
private NodeSizeDetails executorResultToSizeDetail(final Map<String,Object> result, String nodeId, String jobId)
private NodeSizeDetails executorResultToSizeDetail(final Map<String, Object> result, String nodeId, String jobId)
{
if(result.containsKey(NodeSizeDetailActionExecutor.EXCEPTION))
if (result.containsKey(NodeSizeDetailActionExecutor.EXCEPTION))
{
return new NodeSizeDetails(nodeId, COMPLETED.name());
}
// Check for the presence of "size" key.
boolean hasSizeKey = result.containsKey("size");
if (hasSizeKey)
if (result.containsKey("size"))
{
NodeSizeDetails nodeSizeDetails = new NodeSizeDetails((String) result.get("nodeId"),
(Long) result.get("size"),
(String) result.get("calculatedAt"),
(Integer) result.get("numberOfFiles"),
COMPLETED.name(),
(String) result.get(ACTIONID));
NodeSizeDetails nodeSizeDetails =
new NodeSizeDetails((String) result.get("nodeId"), (Long) result.get("size"),
(Date) result.get("calculatedAt"), (Integer) result.get("numberOfFiles"),
COMPLETED.name(), (String) result.get(ACTION_ID));
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;
}
@@ -188,25 +161,9 @@ public class SizeDetailsImpl implements SizeDetails
private void validateType(NodeRef nodeRef) throws InvalidNodeTypeException
{
QName qName = nodeService.getType(nodeRef);
validatePermissions(nodeRef, nodeRef.getId());
if(!FOLDER.equalsIgnoreCase(qName.getLocalName()))
if (!nodes.isSubClass(nodeRef, ContentModel.TYPE_FOLDER, false))
{
throw new InvalidNodeTypeException(INVALID_NODEID);
}
}
/**
* 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");
throw new InvalidNodeTypeException("Invalid parameter: value of nodeId is invalid");
}
}
}

View File

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

View File

@@ -26,6 +26,9 @@
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.SizeDetails;
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.parameters.Parameters;
import org.alfresco.util.ParameterCheck;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
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")
public class NodeSizeDetailsRelation implements
RelationshipResourceAction.ReadById<NodeSizeDetails>,
RelationshipResourceAction.Create<NodeSizeDetails>,
InitializingBean {
public class NodeSizeDetailsRelation implements RelationshipResourceAction.ReadById<NodeSizeDetails>,
RelationshipResourceAction.Create<NodeSizeDetails>, InitializingBean
{
private static final Logger LOG = LoggerFactory.getLogger(NodeSizeDetailsRelation.class);
private Nodes nodes;
private SizeDetails sizeDetails;
@@ -68,27 +65,27 @@ public class NodeSizeDetailsRelation implements
@Override
public void afterPropertiesSet()
{
ParameterCheck.mandatory("nodes", this.nodes);
ParameterCheck.mandatory("sizeDetails", this.sizeDetails);
}
@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.",
kind= ResourceParameter.KIND.HTTP_BODY_OBJECT, allowMultiple=false)
@WebApiParam(name = "nodeSizeEntity", title = "Node Size Details Request",
description = "Request for processing Node Size.", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT,
allowMultiple = false)
@Override
public List<NodeSizeDetails> create(String nodeId, List<NodeSizeDetails> nodeSizeEntity, Parameters parameters)
{
LOG.debug(" Executing generateNodeSizeDetailsRequest method ");
return Arrays.asList(sizeDetails.generateNodeSizeDetailsRequest(nodeId));
return List.of(sizeDetails.generateNodeSizeDetailsRequest(nodeId));
}
@WebApiDescription(title = "Get Node Size Details", description = "Get the Node Size Details")
@WebApiParameters({
@WebApiParam(name="nodeId", title="The unique id of the Node being addressed", description="A single node id"),
@WebApiParam(name="jobId", title="Job Id to get the NodeSizeDetails", description="JobId")})
@WebApiParameters({ @WebApiParam(name = "nodeId", title = "The unique id of the Node being addressed",
description = "A single node id"),
@WebApiParam(name = "jobId", title = "Job Id to get the NodeSizeDetails", description = "JobId") })
@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">
<property name="nodes" ref="nodes" />
<property name="nodeService" ref="NodeService" />
<property name="permissionService" ref="permissionService"/>
<property name="actionService" ref="ActionService"/>
<property name="simpleCache" ref="folderSizeSharedCache" />
<property name="defaultItems" value="${alfresco.restApi.calculateFolderSize.items}"/>

View File

@@ -25,6 +25,13 @@
*/
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.cache.SimpleCache;
import org.alfresco.rest.api.Nodes;
@@ -39,27 +46,19 @@ import org.alfresco.service.namespace.QName;
import org.junit.Before;
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.
*
*/
public class SizeDetailsImplTest
{
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 Nodes nodes;
private NodeService nodeService;
private ActionService actionService;
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
public void setUp()
@@ -106,7 +105,7 @@ public class SizeDetailsImplTest
NodeSizeDetails requestSizeDetails = sizeDetailsImpl.generateNodeSizeDetailsRequest(nodeId);
assertNotNull("After executing POST/size-details, it will provide with 202 status code", requestSizeDetails);
NodeSizeDetails nodeSizeDetails = sizeDetailsImpl.getNodeSizeDetails(nodeId,jobId);
NodeSizeDetails nodeSizeDetails = sizeDetailsImpl.getNodeSizeDetails(nodeId, jobId);
assertNotNull("After executing GET/size-details, it will provide with 200 status code", nodeSizeDetails);
}

View File

@@ -25,12 +25,24 @@
*/
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.model.NodeSizeDetails;
import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.tests.client.HttpResponse;
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.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteVisibility;
@@ -45,20 +57,11 @@ import org.junit.runners.MethodSorters;
import org.slf4j.Logger;
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.
*/
@FixMethodOrder (MethodSorters.NAME_ASCENDING)
@RunWith (JUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(JUnit4.class)
public class NodeSizeDetailsTest extends AbstractBaseApiTest
{
private static final Logger LOG = LoggerFactory.getLogger(NodeSizeDetailsTest.class);
@@ -102,7 +105,7 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
setRequestContext(user1);
String siteTitle = "RandomSite" + System.currentTimeMillis();
this.userOneN1Site = createSite("RN"+RUNID, siteTitle, siteTitle, SiteVisibility.PRIVATE, 201);
this.userOneN1Site = createSite("RN" + RUNID, siteTitle, siteTitle, SiteVisibility.PRIVATE, 201);
// Create a folder within the site document's library.
String folderName = "folder" + System.currentTimeMillis();
@@ -123,9 +126,11 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
// Perform POST request
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);
@@ -136,11 +141,13 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
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());
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
@@ -149,14 +156,14 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
setRequestContext(user1);
UserInfo userInfo = new UserInfo(user1);
String folder0Name = "f0-testParentFolder-"+RUNID;
String parentFolder = createFolder(tDocLibNodeId, folder0Name,null).getId();
String folder0Name = "f0-testParentFolder-" + RUNID;
String parentFolder = createFolder(tDocLibNodeId, folder0Name, null).getId();
for(int i=1;i<=500;i++)
for (int i = 1; i <= 500; i++)
{
String folderBName = "folder"+i+RUNID + "_B";
String folderBName = "folder" + i + RUNID + "_B";
String folderBId = createFolder(parentFolder, folderBName, null).getId();
String fileName = "content"+i+ RUNID + ".txt";
String fileName = "content" + i + RUNID + ".txt";
Document d1 = new Document();
d1.setIsFolder(false);
d1.setParentId(folderBId);
@@ -173,14 +180,17 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
assertEquals(500, nodes.size());
//Start Time before triggering POST/size-details API
LocalTime expectedTime = LocalTime.now().plusSeconds(5);
LocalTime expectedTime = LocalTime.now()
.plusSeconds(5);
// Perform POST request
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);
@@ -191,15 +201,17 @@ public class NodeSizeDetailsTest extends AbstractBaseApiTest
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());
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
LocalTime actualTime = LocalTime.now();
assertTrue("Calculating folder node is taking time greater than 5 seconds ",actualTime.isBefore(expectedTime));
assertTrue("Calculating folder node is taking time greater than 5 seconds ", actualTime.isBefore(expectedTime));
}
/**

View File

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

View File

@@ -45,27 +45,24 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional
public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
{
/**
* Id used to identify the test action created
*/
private final static String ID = GUID.generate();
/**
* The test node reference
*/
private NodeRef nodeRef;
/**
* The folder Size executer
*/
private NodeSizeDetailActionExecutor executer;
/**
* Id used to identify the test action created
*/
private final static String ID = GUID.generate();
/**
* Set the simpleCache service
*
* @param simpleCache the cache service
*/
private SimpleCache<Serializable, Map<String,Object>> simpleCache;
private SimpleCache<Serializable, Map<String, Object>> simpleCache;
/**
* Called at the begining of all tests.
@@ -73,29 +70,26 @@ public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
@Before
public void before() throws Exception
{
NodeService nodeService = (NodeService)this.applicationContext.getBean("nodeService");
NodeService nodeService = (NodeService) this.applicationContext.getBean("nodeService");
StoreRef testStoreRef;
NodeRef rootNodeRef;
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
AuthenticationComponent authenticationComponent =
(AuthenticationComponent) applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
// Create the store and get the root node
testStoreRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_"
+ System.currentTimeMillis());
testStoreRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
rootNodeRef = nodeService.getRootNode(testStoreRef);
// Create the node used for tests
this.nodeRef = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"),
ContentModel.TYPE_CONTENT).getChildRef();
this.nodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"), ContentModel.TYPE_CONTENT).getChildRef();
// Get the executer instance.
this.executer = (NodeSizeDetailActionExecutor)this.applicationContext.getBean(NodeSizeDetailActionExecutor.NAME);
this.executer = (NodeSizeDetailActionExecutor) this.applicationContext.getBean(NodeSizeDetailActionExecutor.NAME);
simpleCache = (SimpleCache<Serializable, Map<String,Object>>) this.applicationContext.getBean("folderSizeSharedCache");
simpleCache = (SimpleCache<Serializable, Map<String, Object>>) this.applicationContext.getBean("folderSizeSharedCache");
}
/**
@@ -109,7 +103,7 @@ public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
action.setParameterValue(NodeSizeDetailActionExecutor.DEFAULT_SIZE, maxItems);
this.executer.executeImpl(action, this.nodeRef);
Object resultAction = simpleCache.get(this.nodeRef.getId());
Map<String, Object> mapResult = (Map<String, Object>)resultAction;
assertTrue(mapResult != null);
Map<String, Object> mapResult = (Map<String, Object>) resultAction;
assertNotNull(mapResult);
}
}