Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)

121907 gjames: RA-774 Renaming action to operation


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@126435 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2016-05-10 10:54:32 +00:00
parent 0632a4db9a
commit bd65bbf529
11 changed files with 71 additions and 76 deletions

View File

@@ -21,7 +21,7 @@ package org.alfresco.rest.api.nodes;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.Node; import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.NodeTarget; import org.alfresco.rest.api.model.NodeTarget;
import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.WebApiParam;
@@ -123,14 +123,14 @@ public class NodesEntityResource implements
nodes.deleteNode(nodeId); nodes.deleteNode(nodeId);
} }
@Action("copy") @Operation("copy")
@WebApiDescription(title = "Copy Node", description="Copy one or more nodes (files or folders) to a new target folder, with option to rename.") @WebApiDescription(title = "Copy Node", description="Copy one or more nodes (files or folders) to a new target folder, with option to rename.")
public Node copyById(String nodeId, NodeTarget target, Parameters parameters) public Node copyById(String nodeId, NodeTarget target, Parameters parameters)
{ {
return nodes.copyNode(nodeId, target.getTargetParentId(), target.getName(), parameters); return nodes.copyNode(nodeId, target.getTargetParentId(), target.getName(), parameters);
} }
@Action("move") @Operation("move")
@WebApiDescription(title = "Move Node", description="Moves one or more nodes (files or folders) to a new target folder, with option to rename.") @WebApiDescription(title = "Move Node", description="Moves one or more nodes (files or folders) to a new target folder, with option to rename.")
public Node moveById(String nodeId, NodeTarget target, Parameters parameters) public Node moveById(String nodeId, NodeTarget target, Parameters parameters)
{ {

View File

@@ -6,13 +6,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* An action as on an entity in the Rest API * An Operation as on an entity in the Rest API
* *
* @author Gethin James * @author Gethin James
*/ */
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Action public @interface Operation
{ {
String value(); String value();
} }

View File

@@ -22,34 +22,33 @@ import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
* Extends ResourceMetaData to give more information about an Action * Extends ResourceMetaData to give more information about an Operation
* *
* @author Gethin James * @author Gethin James
*/ */
public class ActionResourceMetaData extends ResourceMetadata public class OperationResourceMetaData extends ResourceMetadata
{ {
private final Method actionMethod; private final Method operationMethod;
/** /**
* Use this constructor to create the resource metadata * Use this constructor to create the resource metadata
* @param uniqueId * @param uniqueId
* @param operations * @param operations
* @param api * @param api
* @param actionMethod * @param operationMethod
*/ */
public ActionResourceMetaData(String uniqueId, List<ResourceOperation> operations, Api api, Method actionMethod) public OperationResourceMetaData(String uniqueId, List<ResourceOperation> operations, Api api, Method operationMethod)
{ {
super(uniqueId, RESOURCE_TYPE.ACTION, operations, api, null, null, null); super(uniqueId, RESOURCE_TYPE.OPERATION, operations, api, null, null, null);
if (operations.size()!= 1) if (operations.size()!= 1)
{ {
throw new IllegalArgumentException("Only 1 action per url is supported for an entity"); throw new IllegalArgumentException("Only 1 operation per url is supported for an entity");
} }
this.actionMethod = actionMethod; this.operationMethod = operationMethod;
} }
/** /**
@@ -58,22 +57,22 @@ public class ActionResourceMetaData extends ResourceMetadata
* @param api * @param api
* @param apiDeleted * @param apiDeleted
*/ */
public ActionResourceMetaData(String uniqueId, Api api, Set<Class<? extends ResourceAction>> apiDeleted) public OperationResourceMetaData(String uniqueId, Api api, Set<Class<? extends ResourceAction>> apiDeleted)
{ {
super(uniqueId, RESOURCE_TYPE.ACTION, null, api, apiDeleted, null, null); super(uniqueId, RESOURCE_TYPE.OPERATION, null, api, apiDeleted, null, null);
this.actionMethod = null; this.operationMethod = null;
} }
public Method getActionMethod() public Method getOperationMethod()
{ {
return actionMethod; return operationMethod;
} }
@Override @Override
public String toString() public String toString()
{ {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
builder.append("ActionResourceMetaData [api="); builder.append("OperationResourceMetaData [api=");
builder.append(this.getApi()); builder.append(this.getApi());
builder.append(", uniqueId="); builder.append(", uniqueId=");
builder.append(this.getUniqueId()); builder.append(this.getUniqueId());
@@ -85,7 +84,7 @@ public class ActionResourceMetaData extends ResourceMetadata
builder.append(this.getOperations()); builder.append(this.getOperations());
builder.append(", apiDeleted="); builder.append(", apiDeleted=");
builder.append(this.getApiDeleted()); builder.append(this.getApiDeleted());
builder.append("actionMethod=").append(actionMethod); builder.append("operationMethod=").append(operationMethod);
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();
} }

View File

@@ -29,7 +29,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.WebApi; import org.alfresco.rest.framework.WebApi;
@@ -134,7 +134,7 @@ public class ResourceInspector
} }
inspectAddressedProperties(api, resource, urlPath, metainfo); inspectAddressedProperties(api, resource, urlPath, metainfo);
inspectActions(api, resource, urlPath, metainfo); inspectOperations(api, resource, urlPath, metainfo);
return metainfo; return metainfo;
} }
@@ -535,46 +535,46 @@ public class ResourceInspector
} }
/** /**
* Inspect a resource to find actions on it. * Inspect a resource to find operations on it.
* @param api Api * @param api Api
* @param resource Class<?> * @param resource Class<?>
* @param entityPath String * @param entityPath String
* @param metainfo List<ResourceMetadata> * @param metainfo List<ResourceMetadata>
*/ */
public static void inspectActions(Api api, Class<?> resource, final String entityPath, List<ResourceMetadata> metainfo) public static void inspectOperations(Api api, Class<?> resource, final String entityPath, List<ResourceMetadata> metainfo)
{ {
Map<String,Pair<ResourceOperation,Method>> operations = findActions(entityPath, resource); Map<String,Pair<ResourceOperation,Method>> operations = findOperations(entityPath, resource);
if (operations != null && !operations.isEmpty()) if (operations != null && !operations.isEmpty())
{ {
for (Entry<String, Pair<ResourceOperation, Method>> opera : operations.entrySet()) for (Entry<String, Pair<ResourceOperation, Method>> opera : operations.entrySet())
{ {
if (isDeleted(opera.getValue().getSecond())) if (isDeleted(opera.getValue().getSecond()))
{ {
metainfo.add(new ActionResourceMetaData(opera.getKey(), api, new HashSet(Arrays.asList(opera.getValue().getFirst())))); metainfo.add(new OperationResourceMetaData(opera.getKey(), api, new HashSet(Arrays.asList(opera.getValue().getFirst()))));
} }
else else
{ {
metainfo.add(new ActionResourceMetaData(opera.getKey(), Arrays.asList(opera.getValue().getFirst()), api, opera.getValue().getSecond())); metainfo.add(new OperationResourceMetaData(opera.getKey(), Arrays.asList(opera.getValue().getFirst()), api, opera.getValue().getSecond()));
} }
} }
} }
} }
/** /**
* Finds actions on an entity * Finds operations on an entity
* @param entityPath path to the entity * @param entityPath path to the entity
* @param anyClass resource clause * @param anyClass resource clause
* @return The operations * @return The operations
*/ */
private static Map<String,Pair<ResourceOperation,Method>> findActions(String entityPath, Class<?> anyClass) private static Map<String,Pair<ResourceOperation,Method>> findOperations(String entityPath, Class<?> anyClass)
{ {
Map<String, Pair<ResourceOperation,Method>> embeds = new HashMap<String, Pair<ResourceOperation,Method>>(); Map<String, Pair<ResourceOperation,Method>> embeds = new HashMap<String, Pair<ResourceOperation,Method>>();
List<Method> annotatedMethods = ResourceInspectorUtil.findMethodsByAnnotation(anyClass, Action.class); List<Method> annotatedMethods = ResourceInspectorUtil.findMethodsByAnnotation(anyClass, Operation.class);
if (annotatedMethods != null && !annotatedMethods.isEmpty()) if (annotatedMethods != null && !annotatedMethods.isEmpty())
{ {
for (Method annotatedMethod : annotatedMethods) for (Method annotatedMethod : annotatedMethods)
{ {
Annotation annot = AnnotationUtils.findAnnotation(annotatedMethod, Action.class); Annotation annot = AnnotationUtils.findAnnotation(annotatedMethod, Operation.class);
if (annot != null) if (annot != null)
{ {
Map<String, Object> annotAttribs = AnnotationUtils.getAnnotationAttributes(annot); Map<String, Object> annotAttribs = AnnotationUtils.getAnnotationAttributes(annot);

View File

@@ -6,8 +6,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.core.BridgeMethodResolver; import org.springframework.core.BridgeMethodResolver;
@@ -36,13 +35,14 @@ public class ResourceInspectorUtil
/* /*
* The api is consistent that the object passed in must match the object passed out * The api is consistent that the object passed in must match the object passed out
* however, actions are different, they don't even need a param, and if its supplied * however, operations are different, if the param is supplied it doesn't have to match
* then it doesn't have to match. So we need special logic for actions * the return type.
* So we need special logic for operations
*/ */
Annotation annot = AnnotationUtils.findAnnotation(resolvedMethod, Action.class); Annotation annot = AnnotationUtils.findAnnotation(resolvedMethod, Operation.class);
if (annot != null) if (annot != null)
{ {
return determinActionType(resource, method); return determineOperationType(resource, method);
} }
else else
{ {
@@ -55,16 +55,16 @@ public class ResourceInspectorUtil
} }
} }
protected static Class determinActionType(Class resource, Method method) protected static Class determineOperationType(Class resource, Method method)
{ {
//Its an Action annotated method and its a bit special //Its an operation annotated method and its a bit special
Class<?>[] paramTypes = method.getParameterTypes(); Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes!= null) if (paramTypes!= null)
{ {
switch (paramTypes.length) switch (paramTypes.length)
{ {
case 3: case 3:
//EntityResource action by id, same logic as RelationshipEntityResource action by id //EntityResource operation by id, same logic as RelationshipEntityResource operation by id
case 4: case 4:
int position = paramTypes.length-2; int position = paramTypes.length-2;
if (Void.class.equals(paramTypes[position])) if (Void.class.equals(paramTypes[position]))
@@ -78,7 +78,7 @@ public class ResourceInspectorUtil
} }
} }
throw new IllegalArgumentException("An action method signature should have 3 parameters (uniqueId, typePassedin, Parameters)," + throw new IllegalArgumentException("An operation method signature should have 3 parameters (uniqueId, typePassedin, Parameters)," +
" use Void if you are not interested in the second argument."); " use Void if you are not interested in the second argument.");
} }

View File

@@ -36,7 +36,7 @@ import org.springframework.http.HttpMethod;
*/ */
public class ResourceMetadata public class ResourceMetadata
{ {
public enum RESOURCE_TYPE {ENTITY, RELATIONSHIP, PROPERTY, ACTION}; public enum RESOURCE_TYPE {ENTITY, RELATIONSHIP, PROPERTY, OPERATION};
private final String uniqueId; private final String uniqueId;
private final RESOURCE_TYPE type; private final RESOURCE_TYPE type;
private final List<ResourceOperation> operations; private final List<ResourceOperation> operations;

View File

@@ -23,7 +23,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.framework.core.ActionResourceMetaData; import org.alfresco.rest.framework.core.OperationResourceMetaData;
import org.alfresco.rest.framework.core.ResourceInspector; import org.alfresco.rest.framework.core.ResourceInspector;
import org.alfresco.rest.framework.core.ResourceInspectorUtil; import org.alfresco.rest.framework.core.ResourceInspectorUtil;
import org.alfresco.rest.framework.core.ResourceLocator; import org.alfresco.rest.framework.core.ResourceLocator;
@@ -93,15 +93,15 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
Object postedRel = processRequest(resourceMeta, req); Object postedRel = processRequest(resourceMeta, req);
return Params.valueOf(entityId, params, postedRel); return Params.valueOf(entityId, params, postedRel);
} }
case ACTION: case OPERATION:
final String actionName = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_RESOURCE); final String operationName = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_RESOURCE);
if (StringUtils.isNotBlank(entityId) && StringUtils.isNotBlank(actionName)) if (StringUtils.isNotBlank(entityId) && StringUtils.isNotBlank(operationName))
{ {
Class objectType = resourceMeta.getObjectType(HttpMethod.POST); Class objectType = resourceMeta.getObjectType(HttpMethod.POST);
Object postedObj = null; Object postedObj = null;
if (objectType!= null) if (objectType!= null)
{ {
//Actions don't support a List as json body //Operations don't support a List as json body
postedObj = ResourceWebScriptHelper.extractJsonContent(req, jsonHelper, objectType); postedObj = ResourceWebScriptHelper.extractJsonContent(req, jsonHelper, objectType);
} }
return Params.valueOf(entityId, params, postedObj); return Params.valueOf(entityId, params, postedObj);
@@ -170,26 +170,26 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
/** /**
* Execute a generic action method * Execute a generic operation method
* @param resource * @param resource
* @param params * @param params
* @return the result of the execution. * @return the result of the execution.
*/ */
private Object executeAction(ResourceWithMetadata resource, Params params) throws Throwable private Object executeOperation(ResourceWithMetadata resource, Params params) throws Throwable
{ {
ActionResourceMetaData actionResourceMetaData = (ActionResourceMetaData) resource.getMetaData(); OperationResourceMetaData operationResourceMetaData = (OperationResourceMetaData) resource.getMetaData();
switch (actionResourceMetaData.getActionMethod().getParameterTypes().length) switch (operationResourceMetaData.getOperationMethod().getParameterTypes().length)
{ {
case 3: case 3:
//EntityResource action by id //EntityResource operation by id
return ResourceInspectorUtil.invokeMethod(actionResourceMetaData.getActionMethod(),resource.getResource(), params.getEntityId(), params.getPassedIn(), params); return ResourceInspectorUtil.invokeMethod(operationResourceMetaData.getOperationMethod(),resource.getResource(), params.getEntityId(), params.getPassedIn(), params);
case 4: case 4:
//RelationshipEntityResource action by id //RelationshipEntityResource operation by id
return ResourceInspectorUtil.invokeMethod(actionResourceMetaData.getActionMethod(),resource.getResource(), params.getEntityId(), params.getRelationshipId(), params.getPassedIn(), params); return ResourceInspectorUtil.invokeMethod(operationResourceMetaData.getOperationMethod(),resource.getResource(), params.getEntityId(), params.getRelationshipId(), params.getPassedIn(), params);
} }
throw new UnsupportedResourceOperationException("The action method has an invalid signature"); throw new UnsupportedResourceOperationException("The operation method has an invalid signature");
} }
/** /**
@@ -256,8 +256,8 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
return wrapWithCollectionWithPaging(createdRel); return wrapWithCollectionWithPaging(createdRel);
} }
} }
case ACTION: case OPERATION:
return executeAction(resource, params); return executeOperation(resource, params);
default: default:
throw new UnsupportedResourceOperationException("POST not supported for Actions"); throw new UnsupportedResourceOperationException("POST not supported for Actions");
} }

View File

@@ -1,6 +1,6 @@
package org.alfresco.rest.framework.tests.api.mocks; package org.alfresco.rest.framework.tests.api.mocks;
import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter; import org.alfresco.rest.framework.core.ResourceParameter;
@@ -19,12 +19,12 @@ public class GrassEntityResource implements EntityResourceAction.ReadById<Grass>
return new Grass(id); return new Grass(id);
} }
@Action("cut") @Operation("cut")
public String cutLawn(String id, Void notused, Parameters parameters) { public String cutLawn(String id, Void notused, Parameters parameters) {
return "All done"; return "All done";
} }
@Action("grow") @Operation("grow")
@WebApiDescription(title = "Grow the grass") @WebApiDescription(title = "Grow the grass")
@WebApiParam(name = "Grass", title = "The grass.",required=true, kind = ResourceParameter.KIND.HTTP_BODY_OBJECT) @WebApiParam(name = "Grass", title = "The grass.",required=true, kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
public String growTheLawn(String id, Grass grass, Parameters parameters) { public String growTheLawn(String id, Grass grass, Parameters parameters) {

View File

@@ -15,10 +15,8 @@ import java.util.Map;
import org.alfresco.rest.api.model.Comment; import org.alfresco.rest.api.model.Comment;
import org.alfresco.rest.api.nodes.NodeCommentsRelation; import org.alfresco.rest.api.nodes.NodeCommentsRelation;
import org.alfresco.rest.framework.Action;
import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.core.OperationResourceMetaData;
import org.alfresco.rest.framework.core.ActionResourceMetaData;
import org.alfresco.rest.framework.core.ResourceInspector; import org.alfresco.rest.framework.core.ResourceInspector;
import org.alfresco.rest.framework.core.ResourceInspectorUtil; import org.alfresco.rest.framework.core.ResourceInspectorUtil;
import org.alfresco.rest.framework.core.ResourceMetadata; import org.alfresco.rest.framework.core.ResourceMetadata;
@@ -391,20 +389,20 @@ public class InspectorTests
} }
@Test @Test
public void testInspectActions() throws IllegalAccessException, InstantiationException, Throwable public void testInspectOperations() throws IllegalAccessException, InstantiationException, Throwable
{ {
Api api = Api.valueOf("alfrescomock", "private", "1"); Api api = Api.valueOf("alfrescomock", "private", "1");
List<ResourceMetadata> metainfo = new ArrayList<ResourceMetadata>(); List<ResourceMetadata> metainfo = new ArrayList<ResourceMetadata>();
GrassEntityResource grassEntityResource = new GrassEntityResource(); GrassEntityResource grassEntityResource = new GrassEntityResource();
ResourceInspector.inspectActions(api, GrassEntityResource.class,"-root-", metainfo); ResourceInspector.inspectOperations(api, GrassEntityResource.class,"-root-", metainfo);
assertTrue(metainfo.size()==2); assertTrue(metainfo.size()==2);
for (ResourceMetadata resourceMetadata : metainfo) for (ResourceMetadata resourceMetadata : metainfo)
{ {
assertEquals(ResourceMetadata.RESOURCE_TYPE.ACTION, resourceMetadata.getType()); assertEquals(ResourceMetadata.RESOURCE_TYPE.OPERATION, resourceMetadata.getType());
ActionResourceMetaData actionResourceMetaData = (ActionResourceMetaData) resourceMetadata; OperationResourceMetaData operationResourceMetaData = (OperationResourceMetaData) resourceMetadata;
Method actionMethod = actionResourceMetaData.getActionMethod(); Method actionMethod = operationResourceMetaData.getOperationMethod();
String result = null; String result = null;
switch (resourceMetadata.getUniqueId()) switch (resourceMetadata.getUniqueId())
{ {

View File

@@ -432,13 +432,13 @@ public class ParamsExtractorTests
} }
/** /**
* Mocks an action * Mocks an operation
* @return ResourceMetadata a Entity * @return ResourceMetadata a Entity
*/ */
private static ResourceMetadata mockAction() private static ResourceMetadata mockOperation()
{ {
ResourceMetadata resourceMock = mock(ResourceMetadata.class); ResourceMetadata resourceMock = mock(ResourceMetadata.class);
when(resourceMock.getType()).thenReturn(ResourceMetadata.RESOURCE_TYPE.ACTION); when(resourceMock.getType()).thenReturn(ResourceMetadata.RESOURCE_TYPE.OPERATION);
return resourceMock; return resourceMock;
} }

View File

@@ -7,11 +7,9 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -147,7 +145,7 @@ public class ResourceLocatorTests
collResource = locator.locateResource(api, templateVars, HttpMethod.POST); collResource = locator.locateResource(api, templateVars, HttpMethod.POST);
assertEquals(GrassEntityResource.class, collResource.getResource().getClass()); assertEquals(GrassEntityResource.class, collResource.getResource().getClass());
assertEquals(ResourceMetadata.RESOURCE_TYPE.ACTION, collResource.getMetaData().getType()); assertEquals(ResourceMetadata.RESOURCE_TYPE.OPERATION, collResource.getMetaData().getType());
} }
@Test @Test