mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-01 14:41:46 +00:00
[feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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}"/>
|
||||
|
@@ -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);
|
||||
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user