[feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details

This commit is contained in:
mohit-singh4
2024-08-22 12:44:19 +05:30
parent 9f28594ba5
commit a9878d9514
17 changed files with 535 additions and 279 deletions

View File

@@ -0,0 +1,33 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api;
import org.alfresco.rest.api.model.NodeSizeDetails;
public interface SizeDetails
{
NodeSizeDetails calculateNodeSize(String nodeId);
}

View File

@@ -1,77 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.action.executer.NodeSizeActionExecuter;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FolderSizeImpl {
private ActionService actionService;
private static final Logger LOG = LoggerFactory.getLogger(FolderSizeImpl.class);
private static final String IN_PROGRESS = "IN-PROGRESS";
private static final String STATUS = "status";
private static final String MESSAGE = "Request has been acknowledged";
public FolderSizeImpl(ActionService actionService)
{
this.actionService = actionService;
}
public Map<String, Object> executingAsynchronousFolderAction(final NodeRef nodeRef, final int defaultItems, final Map<String, Object> result, final SimpleCache<Serializable, Object> simpleCache)
{
if(simpleCache.get(nodeRef.getId()) == null)
{
executeAction(nodeRef, defaultItems, simpleCache);
}
LOG.debug("Executing NodeSizeActionExecuter from executingAsynchronousFolderAction method");
result.putIfAbsent("message",MESSAGE);
return result;
}
protected void executeAction(NodeRef nodeRef, int defaultItems, SimpleCache<Serializable, Object> simpleCache)
{
Map<String, Object> currentStatus = new HashMap<>();
currentStatus.put(STATUS,IN_PROGRESS);
Action folderSizeAction = actionService.createAction(NodeSizeActionExecuter.NAME);
folderSizeAction.setTrackStatus(true);
folderSizeAction.setExecuteAsynchronously(true);
folderSizeAction.setParameterValue(NodeSizeActionExecuter.DEFAULT_SIZE, defaultItems);
simpleCache.put(nodeRef.getId(),currentStatus);
actionService.executeAction(folderSizeAction, nodeRef, false, true);
}
}

View File

@@ -0,0 +1,180 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
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.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 org.alfresco.repo.action.executer.NodeSizeDetailsActionExecutor;
public class SizeDetailsImpl implements SizeDetails
{
private static final Logger LOG = LoggerFactory.getLogger(SizeDetailsImpl.class);
private static final String IN_PROGRESS = "IN-PROGRESS";
private static final String STATUS = "status";
private static final String COMPLETED = "COMPLETED";
private static final String NOT_INITIATED = "NOT-INITIATED";
private static final String INVALID_NODEID = "Invalid parameter: value of nodeId is invalid";
private static final String FOLDER = "folder";
private Nodes nodes;
private NodeService nodeService;
private PermissionService permissionService;
private ActionService actionService;
private SimpleCache<Serializable,Object> simpleCache;
private int defaultItems;
public void setNodes(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)
{
this.actionService = actionService;
}
public void setSimpleCache(SimpleCache<Serializable, Object> simpleCache)
{
this.simpleCache = simpleCache;
}
public void setDefaultItems(int defaultItems)
{
this.defaultItems = defaultItems;
}
/**
* calculateNodeSize : providing HTTP STATUS 202 which signifies REQUEST ACCEPTED.
* HTTP STATUS 200 will provide the size details response from cache.
*/
public NodeSizeDetails calculateNodeSize(final String nodeId)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
QName qName = nodeService.getType(nodeRef);
validatePermissions(nodeRef, nodeId);
if(!FOLDER.equalsIgnoreCase(qName.getLocalName()))
{
throw new InvalidNodeTypeException(INVALID_NODEID);
}
if(simpleCache.get(nodeRef.getId()) == null)
{
executeAction(nodeRef, defaultItems, simpleCache);
return null;
}
LOG.debug("Executing NodeSizeActionExecuter from calculateNodeSize method");
return executorResultToSizeDetails((Map<String, Object>)simpleCache.get(nodeRef.getId()));
}
/**
* Executing Action Asynchronously.
*/
private void executeAction(NodeRef nodeRef, int defaultItems, SimpleCache<Serializable, Object> simpleCache)
{
Map<String, Object > currentStatus = new HashMap<>();
currentStatus.put(STATUS,IN_PROGRESS);
Action folderSizeAction = actionService.createAction(NodeSizeDetailsActionExecutor.NAME);
folderSizeAction.setTrackStatus(true);
folderSizeAction.setExecuteAsynchronously(true);
folderSizeAction.setParameterValue(NodeSizeDetailsActionExecutor.DEFAULT_SIZE, defaultItems);
simpleCache.put(nodeRef.getId(),currentStatus);
actionService.executeAction(folderSizeAction, nodeRef, false, true);
}
/**
* Converting action executor response to their respective model class.
*/
private NodeSizeDetails executorResultToSizeDetails(final Map<String,Object> result)
{
if (result == null)
{
return new NodeSizeDetails(NOT_INITIATED);
}
else if(result.containsKey(NodeSizeDetailsActionExecutor.EXCEPTION))
{
return new NodeSizeDetails((String) result.get(NodeSizeDetailsActionExecutor.EXCEPTION));
}
// Check for the presence of "size" key.
boolean hasSizeKey = result.containsKey("size");
if (hasSizeKey)
{
return new NodeSizeDetails((String) result.get("nodeId"), (Long) result.get("size"), (String) result.get("calculatedAt"), (Integer) result.get("numberOfFiles"), COMPLETED);
}
else
{
return new NodeSizeDetails(IN_PROGRESS);
}
}
/**
* 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

@@ -0,0 +1,113 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
public class NodeSizeDetails
{
private String nodeId;
private long size;
private String calculatedAt;
private int numberOfFiles;
private String status;
public NodeSizeDetails()
{
super();
}
public NodeSizeDetails(String status)
{
this.status = status;
}
public NodeSizeDetails(String nodeId, long size, String calculatedAt, int numberOfFiles, String status)
{
this.nodeId = nodeId;
this.size = size;
this.calculatedAt = calculatedAt;
this.numberOfFiles = numberOfFiles;
this.status = status;
}
public String getNodeId()
{
return nodeId;
}
public void setNodeId(String nodeId)
{
this.nodeId = nodeId;
}
public long getSize()
{
return size;
}
public void setSize(long size)
{
this.size = size;
}
public String getCalculatedAt()
{
return calculatedAt;
}
public void setCalculatedAt(String calculatedAt)
{
this.calculatedAt = calculatedAt;
}
public int getNumberOfFiles()
{
return numberOfFiles;
}
public void setNumberOfFiles(int numberOfFiles)
{
this.numberOfFiles = numberOfFiles;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
@Override
public String toString() {
return "NodeSizeDetails{" +
"nodeId='" + nodeId + '\'' +
", size='" + size + '\'' +
", calculatedAt='" + calculatedAt + '\'' +
", numberOfFiles=" + numberOfFiles +
", status='" + status + '\'' +
'}';
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -27,46 +27,34 @@ package org.alfresco.rest.api.nodes;
import jakarta.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.impl.FolderSizeImpl;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.api.model.LockInfo;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.NodeTarget;
import org.alfresco.rest.api.model.NodePermissions;
import org.alfresco.rest.api.model.NodeSizeDetails;
import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.WebApiParameters;
import org.alfresco.rest.api.SizeDetails;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.FolderResourceAction;
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
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.alfresco.util.ParameterCheck;
import org.slf4j.Logger;
@@ -84,25 +72,13 @@ import org.springframework.extensions.webscripts.Status;
@EntityResource(name="nodes", title = "Nodes")
public class NodesEntityResource implements
EntityResourceAction.ReadById<Node>, EntityResourceAction.Delete, EntityResourceAction.Update<Node>,
BinaryResourceAction.Read, BinaryResourceAction.Update<Node>, FolderResourceAction.RetrieveFolderSize<Map<String,Object>>, InitializingBean
BinaryResourceAction.Read, BinaryResourceAction.Update<Node>, InitializingBean
{
private static final Logger LOG = LoggerFactory.getLogger(NodesEntityResource.class);
private static final String INVALID_NODEID = "Invalid parameter: value of nodeId is invalid";
private static final String NOT_INITIATED = "NOT-INITIATED";
private static final String STATUS = "status";
private static final String COMPLETED = "COMPLETED";
private static final String FOLDER = "folder";
private static final String EXCEPTION = "Exception";
private Nodes nodes;
private DirectAccessUrlHelper directAccessUrlHelper;
private PermissionService permissionService;
private NodeService nodeService;
private ActionService actionService;
private SimpleCache<Serializable,Object> simpleCache;
private int defaultItems;
private SizeDetails sizeDetails;
public void setNodes(Nodes nodes)
{
@@ -114,31 +90,6 @@ public class NodesEntityResource implements
this.directAccessUrlHelper = directAccessUrlHelper;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
public void setSimpleCache(SimpleCache<Serializable, Object> simpleCache)
{
this.simpleCache = simpleCache;
}
public void setDefaultItems(int defaultItems)
{
this.defaultItems = defaultItems;
}
@Override
public void afterPropertiesSet()
{
@@ -286,34 +237,32 @@ public class NodesEntityResource implements
}
/**
* Folder Size - returns size of a folder.
* Folder Size - returns size details of a folder.
*
* @param nodeId String id of folder - will also accept well-known alias, eg. -root- or -my- or -shared-
* Please refer to OpenAPI spec for more details !
* Returns the response message i.e. Request has been acknowledged with 202
* GET/size endpoint to check if the action's status has been completed, comprising the size of the node in bytes.
* Also, size-details endpoint to check if the action's status has been completed, comprising the size of the node in bytes.
* <p>
* If nodeId does not represent a folder, InvalidNodeTypeException (status 422).
*/
@Operation("calculate-folder-size")
@WebApiDescription(title = "Calculating Folder Size", description = "Calculating size of a folder", successStatus = Status.STATUS_ACCEPTED)
@Operation("request-size-details")
@WebApiDescription(title = "Calculating Folder Size", description = "Calculating size of a folder",successStatus = Status.STATUS_ACCEPTED)
@WebApiParameters({@WebApiParam(name = "nodeId", title = "The unique id of Execution Job", description = "A single nodeId")})
public Map<String, Object> calculateFolderSize(String nodeId, Void ignore, Parameters parameters, WithResponse withResponse)
public NodeSizeDetails calculateFolderSize(String nodeId, Void ignore, Parameters parameters, WithResponse withResponse)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
QName qName = nodeService.getType(nodeRef);
Map<String, Object> result = new HashMap<>();
validatePermissions(nodeRef, nodeId);
if(!FOLDER.equalsIgnoreCase(qName.getLocalName()))
{
throw new InvalidNodeTypeException(INVALID_NODEID);
}
try
{
FolderSizeImpl folderSizeImpl = new FolderSizeImpl(actionService);
return folderSizeImpl.executingAsynchronousFolderAction(nodeRef, defaultItems, result, simpleCache);
NodeSizeDetails nodeSizeDetails = sizeDetails.calculateNodeSize(nodeId);
if(nodeSizeDetails == null)
{
withResponse.setStatus(Status.STATUS_ACCEPTED);
}
else
{
withResponse.setStatus(Status.STATUS_OK);
}
return nodeSizeDetails;
}
catch (Exception alfrescoRuntimeError)
{
@@ -322,64 +271,4 @@ public class NodesEntityResource implements
}
}
@Override
@WebApiDescription(title = "Returns Folder Node Size", description = "Returning a Folder Node Size")
@WebApiParameters({@WebApiParam(name = "nodeId", title = "The unique id of Execution Job", description = "A single nodeId")})
@BinaryProperties({"size"})
public Map<String, Object> getFolderSize(String nodeId) throws EntityNotFoundException
{
Map<String, Object> result = new HashMap<>();
try
{
LOG.debug("Retrieving OUTPUT from NodeSizeActionExecutor - NodesEntityResource:getFolderSize");
NodeRef nodeRef = nodes.validateNode(nodeId);
validatePermissions(nodeRef, nodeId);
QName qName = nodeService.getType(nodeRef);
if(!FOLDER.equalsIgnoreCase(qName.getLocalName()))
{
throw new InvalidNodeTypeException(INVALID_NODEID);
}
Map<String, Object> cachedResult = (Map<String, Object>) simpleCache.get(nodeId);
if(cachedResult != null)
{
if(cachedResult.containsKey("size"))
{
cachedResult.put(STATUS, COMPLETED);
result = cachedResult;
}
else
{
result = cachedResult;
}
}
else
{
result.put(STATUS,NOT_INITIATED);
}
return result;
}
catch (Exception ex)
{
LOG.error("Exception occurred in NodesEntityResource:getFolderSize {}", ex.getMessage());
throw ex; // Rethrow with original stack trace
}
}
/**
* 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

@@ -64,7 +64,6 @@ import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelation
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.FolderResourceAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
@@ -111,8 +110,6 @@ public class ResourceInspector
ALL_ENTITY_RESOURCE_INTERFACES.add(MultiPartResourceAction.Create.class);
ALL_ENTITY_RESOURCE_INTERFACES.add(FolderResourceAction.RetrieveFolderSize.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Create.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Read.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.ReadById.class);
@@ -136,7 +133,6 @@ public class ResourceInspector
ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.ReadWithResponse.class);
ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.DeleteWithResponse.class);
ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.UpdateWithResponse.class);
ALL_PROPERTY_RESOURCE_INTERFACES.add(FolderResourceAction.RetrieveFolderSize.class);
}
/**
@@ -224,8 +220,6 @@ public class ResourceInspector
findOperation(RelationshipResourceBinaryAction.DeleteWithResponse.class, DELETE, helperForAddressProps);
findOperation(RelationshipResourceBinaryAction.UpdateWithResponse.class, PUT, helperForAddressProps);
findOperation(FolderResourceAction.RetrieveFolderSize.class, GET, helperForAddressProps);
boolean noAuth = resource.isAnnotationPresent(WebApiNoAuth.class);
if (noAuth)
{

View File

@@ -36,7 +36,6 @@ import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAct
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction.ReadById;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.actions.interfaces.FolderResourceAction;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Params;
@@ -59,8 +58,6 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
{
private static Log logger = LogFactory.getLog(ResourceWebScriptGet.class);
private static final String GET_FOLDERSIZE = "/nodes/{id}/size";
public ResourceWebScriptGet()
{
super();
@@ -228,7 +225,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
}
}
case RELATIONSHIP:
if (StringUtils.isBlank(params.getRelationshipId()) || params.isCollectionResource())
if (StringUtils.isBlank(params.getRelationshipId()) || (params.isCollectionResource()))
{
// Get the collection
if (RelationshipResourceAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
@@ -282,18 +279,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
case PROPERTY:
if (StringUtils.isNotBlank(params.getEntityId()))
{
if (FolderResourceAction.RetrieveFolderSize.class.isAssignableFrom(resource.getResource().getClass())
&& GET_FOLDERSIZE.equals(resource.getMetaData().getUniqueId()))
{
if (resource.getMetaData().isDeleted(FolderResourceAction.RetrieveFolderSize.class))
{
throw new DeletedResourceException("(GET) "+resource.getMetaData().getUniqueId());
}
FolderResourceAction.RetrieveFolderSize getter = (FolderResourceAction.RetrieveFolderSize) resource.getResource();
Object result = getter.getFolderSize(params.getEntityId());
return result;
}
else if (BinaryResourceAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
if (BinaryResourceAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
{
if (resource.getMetaData().isDeleted(BinaryResourceAction.Read.class))
{
@@ -303,7 +289,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
BinaryResource prop = getter.readProperty(params.getEntityId(), params);
return prop;
}
else if (BinaryResourceAction.ReadWithResponse.class.isAssignableFrom(resource.getResource().getClass()))
if (BinaryResourceAction.ReadWithResponse.class.isAssignableFrom(resource.getResource().getClass()))
{
if (resource.getMetaData().isDeleted(BinaryResourceAction.ReadWithResponse.class))
{
@@ -313,7 +299,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
BinaryResource prop = getter.readProperty(params.getEntityId(), params, withResponse);
return prop;
}
else if (RelationshipResourceBinaryAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
if (RelationshipResourceBinaryAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
{
if (resource.getMetaData().isDeleted(RelationshipResourceBinaryAction.Read.class))
{
@@ -323,7 +309,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
BinaryResource prop = getter.readProperty(params.getEntityId(), params.getRelationshipId(), params);
return prop;
}
else if (RelationshipResourceBinaryAction.ReadWithResponse.class.isAssignableFrom(resource.getResource().getClass()))
if (RelationshipResourceBinaryAction.ReadWithResponse.class.isAssignableFrom(resource.getResource().getClass()))
{
if (resource.getMetaData().isDeleted(RelationshipResourceBinaryAction.ReadWithResponse.class))
{
@@ -333,10 +319,6 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
BinaryResource prop = getter.readProperty(params.getEntityId(), params.getRelationshipId(), params, withResponse);
return prop;
}
else
{
throw new UnsupportedResourceOperationException();
}
}
else
{
@@ -345,7 +327,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
default:
throw new UnsupportedResourceOperationException("GET not supported for Actions");
}
}
}

View File

@@ -996,6 +996,29 @@
</property>
</bean>
<bean id="sizeDetails" 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}"/>
</bean>
<bean id="SizeDetails" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.rest.api.SizeDetails</value>
</property>
<property name="target">
<ref bean="sizeDetails" />
</property>
<property name="interceptorNames">
<list>
<idref bean="legacyExceptionInterceptor" />
</list>
</property>
</bean>
<bean class="org.alfresco.rest.api.rules.NodeRuleSettingsRelation">
<property name="ruleSettings" ref="RuleSettings" />
</bean>
@@ -1148,11 +1171,7 @@
<bean class="org.alfresco.rest.api.nodes.NodesEntityResource">
<property name="nodes" ref="Nodes" />
<property name="directAccessUrlHelper" ref="directAccessUrlHelper" />
<property name="permissionService" ref="permissionService"/>
<property name="nodeService" ref="NodeService" />
<property name="actionService" ref="ActionService"/>
<property name="simpleCache" ref="folderSizeSharedCache" />
<property name="defaultItems" value="${alfresco.restApi.calculateFolderSize.items}"/>
<property name="sizeDetails" ref="SizeDetails" />
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeCommentsRelation">

View File

@@ -25,6 +25,7 @@
*/
package org.alfresco;
import org.alfresco.rest.api.tests.NodeSizeDetailsTest;
import org.alfresco.util.testing.category.DBTests;
import org.alfresco.util.testing.category.NonBuildTests;
import org.junit.experimental.categories.Categories;
@@ -76,7 +77,7 @@ import org.junit.runners.Suite;
org.alfresco.rest.api.tests.BufferedResponseTest.class,
org.alfresco.rest.workflow.api.tests.DeploymentWorkflowApiTest.class,
org.alfresco.rest.workflow.api.tests.ProcessDefinitionWorkflowApiTest.class,
org.alfresco.rest.api.tests.NodeFolderSizeApiTest.class
NodeSizeDetailsTest.class
})
public class AppContext02TestSuite
{

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -77,7 +77,8 @@ import org.junit.runners.Suite;
org.alfresco.repo.web.scripts.node.NodeWebScripTest.class,
org.alfresco.rest.api.impl.CommentsImplUnitTest.class,
org.alfresco.rest.api.impl.DownloadsImplCheckArchiveStatusUnitTest.class,
org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class
org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class,
org.alfresco.rest.api.impl.SizeDetailsImplTest.class
})
public class AppContext04TestSuite
{

View File

@@ -0,0 +1,120 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.NodeSizeDetails;
import org.alfresco.rest.api.tests.AbstractBaseApiTest;
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.UserInfo;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PermissionService;
import org.junit.Before;
import org.junit.Test;
import java.io.Serializable;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link SizeDetailsImpl} class.
*
*/
public class SizeDetailsImplTest extends AbstractBaseApiTest
{
private SizeDetailsImpl sizeDetailsImpl;
private Nodes nodes;
private NodeService nodeService;
private PermissionService permissionService;
private ActionService actionService;
private SimpleCache<Serializable,Object> simpleCache;
private final int defaultItems = 1000;
@Before
public void setUp()
{
sizeDetailsImpl = new SizeDetailsImpl();
nodes = mock(Nodes.class);
nodeService = mock(NodeService.class);
permissionService = mock(PermissionService.class);
actionService = mock(ActionService.class);
simpleCache = mock(SimpleCache.class);
sizeDetailsImpl.setNodes(nodes);
sizeDetailsImpl.setNodeService(nodeService);
sizeDetailsImpl.setPermissionService(permissionService);
sizeDetailsImpl.setActionService(actionService);
sizeDetailsImpl.setSimpleCache(simpleCache);
sizeDetailsImpl.setDefaultItems(defaultItems);
}
@Test
public void calculateNodeSize() throws Exception
{
setRequestContext(user1);
UserInfo userInfo = new UserInfo(user1);
String fileName = "content.txt";
String folder0Name = "f0-testParentFolder-"+RUNID;
String parentFolder = createFolder(tDocLibNodeId, folder0Name,null).getId();
NodeRef nodeRef = new NodeRef("protocol", "identifier", parentFolder);
Document d1 = new Document();
d1.setIsFolder(false);
d1.setParentId(parentFolder);
d1.setName(fileName);
d1.setNodeType(TYPE_CM_CONTENT);
d1.setContent(createContentInfo());
d1.setCreatedByUser(userInfo);
d1.setModifiedByUser(userInfo);
when(nodes.validateNode(parentFolder)).thenReturn(nodeRef);
NodeSizeDetails nodeSizeDetails = sizeDetailsImpl.calculateNodeSize(parentFolder);
assertNull("After executing POST/request-size-details first time, it will provide null with 202 status code",nodeSizeDetails);
}
// Method to create content info
private ContentInfo createContentInfo()
{
ContentInfo ciExpected = new ContentInfo();
ciExpected.setMimeType("text/plain");
ciExpected.setMimeTypeName("Plain Text");
ciExpected.setSizeInBytes(44500L);
ciExpected.setEncoding("ISO-8859-1");
return ciExpected;
}
@Override
public String getScope() {
return "public";
}
}

View File

@@ -105,7 +105,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
private static final String URL_CHILDREN = "children";
private static final String URL_CONTENT = "content";
private static final String URL_CALCULATEFOLDERSIZE = "calculate-folder-size";
private static final String URL_CALCULATEFOLDERSIZE = "request-size-details";
protected static final String TYPE_CM_FOLDER = "cm:folder";
protected static final String TYPE_CM_CONTENT = "cm:content";

View File

@@ -27,7 +27,6 @@ package org.alfresco.rest.api.tests;
import org.alfresco.rest.api.model.NodeTarget;
import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.nodes.NodesEntityResource;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.data.ContentInfo;
@@ -53,16 +52,18 @@ import java.util.Map;
import java.util.UUID;
import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* V1 REST API tests for Folder size
* V1 REST API tests for calculating and retrieving Folder size.
*/
@FixMethodOrder (MethodSorters.NAME_ASCENDING)
@RunWith (JUnit4.class)
public class NodeFolderSizeApiTest extends AbstractBaseApiTest
public class NodeSizeDetailsTest extends AbstractBaseApiTest
{
private static final Logger LOG = LoggerFactory.getLogger(NodeFolderSizeApiTest.class);
private static final Logger LOG = LoggerFactory.getLogger(NodeSizeDetailsTest.class);
private Site userOneN1Site;
private String folderId;
@@ -88,7 +89,7 @@ public class NodeFolderSizeApiTest extends AbstractBaseApiTest
}
catch (Exception e)
{
LOG.error("Exception occured in NodeFolderSizeApiTest:addToDocumentLibrary {}", e.getMessage());
LOG.error("Exception occured in NodeSizeDetailsTest:addToDocumentLibrary {}", e.getMessage());
}
return null;
}
@@ -109,10 +110,8 @@ public class NodeFolderSizeApiTest extends AbstractBaseApiTest
}
/**
* Test case for POST/calculate-folder-size, which calculates Folder Size.
* Test case for GET/size, which receive Folder Size.
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/calculate-folder-size}
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/size}
* Test case for POST/request-size-details, which calculates and retrieve size of a folder.
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/request-size-details}
*/
@Test
public void testPostAndGetFolderSize() throws Exception
@@ -120,7 +119,7 @@ public class NodeFolderSizeApiTest extends AbstractBaseApiTest
// Prepare parameters
Map<String, String> params = new HashMap<>();
params.put("nodeId", folderId);
params.put("maxItems", "100");
params.put("maxItems", "1000");
// Perform POST request
HttpResponse postResponse = post(getCalculateFolderSizeUrl(folderId), toJsonAsStringNonNull(params), 202);
@@ -135,7 +134,7 @@ public class NodeFolderSizeApiTest extends AbstractBaseApiTest
Object document = RestApiUtil.parseRestApiEntry(postResponse.getJsonResponse(), Object.class);
assertNotNull("Parsed document should not be null", document);
HttpResponse getResponse = getSingle(NodesEntityResource.class, folderId + "/size", null, 200);
HttpResponse getResponse = post(getCalculateFolderSizeUrl(folderId), toJsonAsStringNonNull(params), 200);
String getJsonResponse = String.valueOf(getResponse.getJsonResponse());
assertNotNull("JSON response should not be null", getJsonResponse);
@@ -192,7 +191,7 @@ public class NodeFolderSizeApiTest extends AbstractBaseApiTest
Object document = RestApiUtil.parseRestApiEntry(postResponse.getJsonResponse(), Object.class);
assertNotNull("Parsed document should not be null", document);
HttpResponse getResponse = getSingle(NodesEntityResource.class, parentFolder + "/size", null, 200);
HttpResponse getResponse = post(getCalculateFolderSizeUrl(parentFolder), toJsonAsStringNonNull(params), 200);
String getJsonResponse = String.valueOf(getResponse.getJsonResponse());
assertNotNull("JSON response should not be null", getJsonResponse);

View File

@@ -49,20 +49,20 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* NodeSizeActionExecuter
* NodeSizeDetailsActionExecutor
* Executing Alfresco FTS Query to find size of Folder Node
*/
public class NodeSizeActionExecuter extends ActionExecuterAbstractBase
public class NodeSizeDetailsActionExecutor extends ActionExecuterAbstractBase
{
private static final Logger LOG = LoggerFactory.getLogger(NodeSizeActionExecuter.class);
private static final Logger LOG = LoggerFactory.getLogger(NodeSizeDetailsActionExecutor.class);
/**
* Action constants
*/
public static final String NAME = "folder-size";
private static final String EXCEPTION = "Exception";
public static final String EXCEPTION = "Exception";
public static final String DEFAULT_SIZE = "default-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\"";
@@ -160,13 +160,13 @@ public class NodeSizeActionExecuter extends ActionExecuterAbstractBase
}
catch (RuntimeException runtimeException)
{
LOG.error("Exception occurred in NodeSizeActionExecutor:results {}", runtimeException.getMessage());
LOG.error("Exception occurred in NodeSizeDetailsActionExecutor:results {}", runtimeException.getMessage());
response.put(EXCEPTION,runtimeException.getMessage());
simpleCache.put(nodeRef.getId(),response);
throw runtimeException;
}
LOG.debug(" Calculating size of Folder Node - NodeSizeActionExecutor:executeImpl ");
LOG.debug(" Calculating size of Folder Node - NodeSizeDetailsActionExecutor:executeImpl ");
final LocalDateTime eventTimestamp = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss");
String formattedTimestamp = eventTimestamp.format(formatter);
@@ -174,6 +174,7 @@ public class NodeSizeActionExecuter extends ActionExecuterAbstractBase
response.put("size", totalSizeFromFacet);
response.put("calculatedAt", formattedTimestamp);
response.put("numberOfFiles", results != null ? results.getNodeRefs().size() : 0);
if(isCalculationCompleted)
{
simpleCache.put(nodeRef.getId(),response);

View File

@@ -789,7 +789,7 @@
</property>
</bean>
<bean id="folder-size" class="org.alfresco.repo.action.executer.NodeSizeActionExecuter" parent="action-executer">
<bean id="folder-size" class="org.alfresco.repo.action.executer.NodeSizeDetailsActionExecutor" parent="action-executer">
<property name="searchService" ref="searchService"/>
<property name="simpleCache" ref="folderSizeSharedCache" />
</bean>

View File

@@ -25,6 +25,7 @@
*/
package org.alfresco;
import org.alfresco.repo.action.executer.NodeSizeDetailsActionExecutorTest;
import org.alfresco.util.testing.category.DBTests;
import org.alfresco.util.testing.category.NonBuildTests;
import org.junit.experimental.categories.Categories;
@@ -78,7 +79,7 @@ import org.junit.runners.Suite;
org.alfresco.repo.activities.SiteActivityTestCaseInsensitivity.class,
org.alfresco.repo.admin.registry.RegistryServiceImplTest.class,
org.alfresco.repo.bootstrap.DataDictionaryFolderTest.class,
org.alfresco.repo.action.executer.NodeSizeActionExecuterTest.class
NodeSizeDetailsActionExecutorTest.class
})
public class AppContext01TestSuite
{

View File

@@ -43,7 +43,7 @@ import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class NodeSizeActionExecuterTest extends BaseSpringTest
public class NodeSizeDetailsActionExecutorTest extends BaseSpringTest
{
/**
* The test node reference
@@ -53,7 +53,7 @@ public class NodeSizeActionExecuterTest extends BaseSpringTest
/**
* The folder Size executer
*/
private NodeSizeActionExecuter executer;
private NodeSizeDetailsActionExecutor executer;
/**
* Id used to identify the test action created
@@ -93,7 +93,7 @@ public class NodeSizeActionExecuterTest extends BaseSpringTest
ContentModel.TYPE_CONTENT).getChildRef();
// Get the executer instance.
this.executer = (NodeSizeActionExecuter)this.applicationContext.getBean(NodeSizeActionExecuter.NAME);
this.executer = (NodeSizeDetailsActionExecutor)this.applicationContext.getBean(NodeSizeDetailsActionExecutor.NAME);
simpleCache = (SimpleCache<Serializable, Object>) this.applicationContext.getBean("folderSizeSharedCache");
}
@@ -104,9 +104,9 @@ public class NodeSizeActionExecuterTest extends BaseSpringTest
@Test
public void testExecution()
{
int maxItems = 100;
ActionImpl action = new ActionImpl(null, ID, NodeSizeActionExecuter.NAME, null);
action.setParameterValue(NodeSizeActionExecuter.DEFAULT_SIZE, maxItems);
int maxItems = 1000;
ActionImpl action = new ActionImpl(null, ID, NodeSizeDetailsActionExecutor.NAME, null);
action.setParameterValue(NodeSizeDetailsActionExecutor.DEFAULT_SIZE, maxItems);
this.executer.executeImpl(action, this.nodeRef);
Object resultAction = simpleCache.get(this.nodeRef.getId());
Map<String, Object> mapResult = (Map<String, Object>)resultAction;