From 096b13dc40e67a73ba2e7259a864c1c0a8bf88d3 Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 11 May 2016 11:15:01 +0000 Subject: [PATCH] Merged HEAD (5.2) to 5.2.N (5.2.1) 126420 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2) 121807 gjames: RA-774 Added basic action support git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126766 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org/alfresco/rest/framework/Action.java | 19 +++++ .../framework/core/ResourceInspector.java | 72 +++++++++++++++-- .../framework/core/ResourceInspectorUtil.java | 2 +- .../rest/framework/core/ResourceMetadata.java | 2 +- .../tests/api/mocks/GrassEntityResource.java | 13 +++ .../framework/tests/core/InspectorTests.java | 79 ++++++++++++++++++- .../tests/core/ParamsExtractorTests.java | 12 +++ .../tests/core/ResourceLocatorTests.java | 28 ++++++- 8 files changed, 216 insertions(+), 11 deletions(-) create mode 100755 source/java/org/alfresco/rest/framework/Action.java diff --git a/source/java/org/alfresco/rest/framework/Action.java b/source/java/org/alfresco/rest/framework/Action.java new file mode 100755 index 0000000000..14eac1aded --- /dev/null +++ b/source/java/org/alfresco/rest/framework/Action.java @@ -0,0 +1,19 @@ +package org.alfresco.rest.framework; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An action as on an entity in the Rest API + * + * @author Gethin James + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Action +{ + String value(); +} + diff --git a/source/java/org/alfresco/rest/framework/core/ResourceInspector.java b/source/java/org/alfresco/rest/framework/core/ResourceInspector.java index a303ff18db..3fd6cb6fbc 100644 --- a/source/java/org/alfresco/rest/framework/core/ResourceInspector.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceInspector.java @@ -37,6 +37,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.WebApi; @@ -134,12 +135,12 @@ public class ResourceInspector helper.operations, api, helper.apiDeleted, null)); } } - + inspectAddressedProperties(api, resource, urlPath, metainfo); + inspectActions(api, resource, urlPath, metainfo); return metainfo; } - /** * Inspects the entity resource and returns meta data about any addresssed/binary properties * @param api Api @@ -323,8 +324,7 @@ public class ResourceInspector } if (paramsCount(params,ResourceParameter.KIND.HTTP_BODY_OBJECT) == 0) { - Class dType = ResourceInspectorUtil.determineType(resource,aMethod); - params.add(ResourceParameter.valueOf(dType.getSimpleName().toUpperCase(), "The entity", "Unique entity properties", true, ResourceParameter.KIND.HTTP_BODY_OBJECT, true, dType)); + inspectBodyParamAndReturnType(resource, aMethod, params); } break; case PUT: @@ -339,8 +339,7 @@ public class ResourceInspector } if (paramsCount(params,ResourceParameter.KIND.HTTP_BODY_OBJECT)== 0) { - Class dType = ResourceInspectorUtil.determineType(resource,aMethod); - params.add(ResourceParameter.valueOf(dType.getSimpleName().toUpperCase(), "The entity", "Unique entity properties", true, ResourceParameter.KIND.HTTP_BODY_OBJECT, true, dType)); + inspectBodyParamAndReturnType(resource, aMethod, params); } break; case GET: @@ -385,6 +384,12 @@ public class ResourceInspector return params; } + private static void inspectBodyParamAndReturnType(Class resource, Method aMethod, List params) + { + Class dType = ResourceInspectorUtil.determineType(resource,aMethod); + params.add(ResourceParameter.valueOf(dType.getSimpleName().toUpperCase(), "The entity", "Unique entity properties", true, KIND.HTTP_BODY_OBJECT, true, dType)); + } + /** * Indicates the number of params of the Kind specified @@ -536,6 +541,61 @@ public class ResourceInspector return embeds; } + /** + * Inspect a resource to find actions on it. + * @param api Api + * @param resource Class + * @param entityPath String + * @param metainfo List + */ + public static void inspectActions(Api api, Class resource, final String entityPath, List metainfo) + { + Map> operations = findActions(entityPath, resource); + if (operations != null && !operations.isEmpty()) + { + for (Entry> opera : operations.entrySet()) + { + if (isDeleted(opera.getValue().getSecond())) + { + metainfo.add(new ResourceMetadata(opera.getKey(), RESOURCE_TYPE.ACTION, null, api, new HashSet(Arrays.asList(opera.getValue().getFirst())), null)); + } + else + { + metainfo.add(new ResourceMetadata(opera.getKey(), RESOURCE_TYPE.ACTION, Arrays.asList(opera.getValue().getFirst()), api, null, null)); + } + } + } + } + + /** + * Finds actions on an entity + * @param entityPath path to the entity + * @param anyClass resource clause + * @return The operations + */ + private static Map> findActions(String entityPath, Class anyClass) + { + Map> embeds = new HashMap>(); + List annotatedMethods = ResourceInspectorUtil.findMethodsByAnnotation(anyClass, Action.class); + if (annotatedMethods != null && !annotatedMethods.isEmpty()) + { + for (Method annotatedMethod : annotatedMethods) + { + Annotation annot = AnnotationUtils.findAnnotation(annotatedMethod, Action.class); + if (annot != null) + { + Map annotAttribs = AnnotationUtils.getAnnotationAttributes(annot); + String actionName = String.valueOf(annotAttribs.get("value")); + String actionPath = ResourceDictionary.resourceKey(entityPath,actionName); + ResourceOperation ro = inspectOperation(anyClass, annotatedMethod, HttpMethod.POST); + embeds.put(actionPath, new Pair(ro,annotatedMethod)); + } + } + + } + return embeds; + } + /** * Inspects the resource to determine what api it belongs to. * It does this by looking for the WebApi package annotation. diff --git a/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java b/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java index 3c943cc737..8d2f22fb94 100755 --- a/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java @@ -78,7 +78,7 @@ public class ResourceInspectorUtil * @return - the List of Method or an empty List */ @SuppressWarnings("rawtypes") - protected static List findMethodsByAnnotation(Class objClass, Class annotationType) + public static List findMethodsByAnnotation(Class objClass, Class annotationType) { List annotatedMethods = new ArrayList(); diff --git a/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java b/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java index 67d75b33dd..8f753385a2 100644 --- a/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java @@ -42,7 +42,7 @@ import org.springframework.http.HttpMethod; */ public class ResourceMetadata { - public enum RESOURCE_TYPE {ENTITY,RELATIONSHIP, PROPERTY}; + public enum RESOURCE_TYPE {ENTITY, RELATIONSHIP, PROPERTY, ACTION}; private final String uniqueId; private final RESOURCE_TYPE type; private final List operations; diff --git a/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java b/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java index 52959fdda5..c499b1b690 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java +++ b/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java @@ -25,6 +25,7 @@ */ package org.alfresco.rest.framework.tests.api.mocks; +import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.resource.EntityResource; @@ -42,4 +43,16 @@ public class GrassEntityResource implements EntityResourceAction.ReadById return new Grass(id); } + @Action("cut") + @WebApiDescription(title = "Cuts the grass") + public String cutLawn(String id, Parameters parameters) { + return "All done"; + } + + @Action("grow") + @WebApiDescription(title = "Grow the grass") + public String growTheLawn(String id, Grass grass, Parameters parameters) { + return "Growing well"; + } + } diff --git a/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java b/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java index 3876d2b3d8..355a1e5502 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java +++ b/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java @@ -40,13 +40,17 @@ import java.util.Map; import org.alfresco.rest.api.model.Comment; import org.alfresco.rest.api.nodes.NodeCommentsRelation; +import org.alfresco.rest.framework.Action; import org.alfresco.rest.framework.Api; +import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.core.ResourceInspector; +import org.alfresco.rest.framework.core.ResourceInspectorUtil; import org.alfresco.rest.framework.core.ResourceMetadata; import org.alfresco.rest.framework.core.ResourceOperation; import org.alfresco.rest.framework.core.ResourceParameter; 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.RelationshipResourceAction; import org.alfresco.rest.framework.tests.api.mocks.Farmer; import org.alfresco.rest.framework.tests.api.mocks.GoatEntityResource; import org.alfresco.rest.framework.tests.api.mocks.Grass; @@ -61,6 +65,7 @@ import org.alfresco.rest.framework.tests.api.mocks.SheepNoActionEntityResource; import org.alfresco.rest.framework.tests.api.mocks2.FarmersDaughter; import org.alfresco.rest.framework.tests.api.mocks2.FarmersGrandson; import org.alfresco.rest.framework.tests.api.mocks2.FarmersSon; +import org.alfresco.rest.framework.tests.api.mocks3.Flock; import org.alfresco.rest.framework.tests.api.mocks3.FlockEntityResource; import org.alfresco.rest.framework.tests.api.mocks3.FlocketEntityResource; import org.alfresco.rest.framework.tests.api.mocks3.GrassEntityResourceNowDeleted; @@ -358,13 +363,83 @@ public class InspectorTests op = ResourceInspector.inspectOperation(FlockEntityResource.class, aMethod, HttpMethod.GET); assertNotNull(op); assertTrue(op.getTitle().startsWith("Deletes a photo")); - + aMethod = ResourceInspector.findMethod(BinaryResourceAction.Update.class, FlockEntityResource.class); op = ResourceInspector.inspectOperation(FlockEntityResource.class, aMethod, HttpMethod.GET); assertNotNull(op); assertTrue(op.getTitle().startsWith("Updates a photo")); } - + + @Test + public void testInspectBodyParam() + { + Method aMethod = ResourceInspector.findMethod(BinaryResourceAction.Update.class, FlockEntityResource.class); + ResourceOperation op = ResourceInspector.inspectOperation(FlockEntityResource.class, aMethod, HttpMethod.PUT); + assertNotNull(op); + List params = op.getParameters(); + assertNotNull(params); + for (ResourceParameter param:params) + { + if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) + { + assertEquals(Flock.class, param.getDataType()); + } + } + + aMethod = ResourceInspector.findMethod(RelationshipResourceAction.Create.class, SheepBlackSheepResource.class); + op = ResourceInspector.inspectOperation(SheepBlackSheepResource.class, aMethod, HttpMethod.POST); + assertNotNull(op); + params = op.getParameters(); + assertNotNull(params); + for (ResourceParameter param:params) + { + if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) + { + assertEquals(Sheep.class, param.getDataType()); + } + } + + aMethod = ResourceInspector.findMethod(EntityResourceAction.Update.class, SheepEntityResourceWithDeletedMethods.class); + op = ResourceInspector.inspectOperation(SheepEntityResourceWithDeletedMethods.class, aMethod, HttpMethod.POST); + assertNotNull(op); + params = op.getParameters(); + assertNotNull(params); + for (ResourceParameter param:params) + { + if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) + { + assertEquals(Sheep.class, param.getDataType()); + } + } + } + + @Test + public void testInspectActions() + { + Api api = Api.valueOf("alfrescomock", "private", "1"); + List metainfo = new ArrayList(); + + ResourceInspector.inspectActions(api, GrassEntityResource.class,"-root-", metainfo); + assertTrue(metainfo.size()==2); + for (ResourceMetadata resourceMetadata : metainfo) + { + assertEquals(ResourceMetadata.RESOURCE_TYPE.ACTION, resourceMetadata.getType()); + switch (resourceMetadata.getUniqueId()) + { + case "/-root-/{entityId}/grow": + assertTrue("GrassEntityResource supports POST", resourceMetadata.supports(HttpMethod.POST)); + assertFalse("GrassEntityResource does not support DELETE", resourceMetadata.supports(HttpMethod.DELETE)); + break; + case "/-root-/{entityId}/cut": + assertTrue("GrassEntityResource supports POST", resourceMetadata.supports(HttpMethod.POST)); + assertFalse("GrassEntityResource does not support GET", resourceMetadata.supports(HttpMethod.GET)); + break; + default: + fail("Invalid action information."); + } + } + } + @Test public void testInspectAddressedProperties() { diff --git a/source/test-java/org/alfresco/rest/framework/tests/core/ParamsExtractorTests.java b/source/test-java/org/alfresco/rest/framework/tests/core/ParamsExtractorTests.java index 550075df7e..7e490e1626 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/core/ParamsExtractorTests.java +++ b/source/test-java/org/alfresco/rest/framework/tests/core/ParamsExtractorTests.java @@ -455,6 +455,18 @@ public class ParamsExtractorTests //when(resourceMock.getObjectType(HttpMethod.POST)).thenReturn(Farmer.class); return resourceMock; } + + /** + * Mocks an action + * @return ResourceMetadata a Entity + */ + private static ResourceMetadata mockAction() + { + ResourceMetadata resourceMock = mock(ResourceMetadata.class); + when(resourceMock.getType()).thenReturn(ResourceMetadata.RESOURCE_TYPE.ACTION); + return resourceMock; + } + /** * Mocks a Relationship Resource * @return ResourceMetadata a Relationship diff --git a/source/test-java/org/alfresco/rest/framework/tests/core/ResourceLocatorTests.java b/source/test-java/org/alfresco/rest/framework/tests/core/ResourceLocatorTests.java index a6fc6f3ac9..07cd8daecc 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/core/ResourceLocatorTests.java +++ b/source/test-java/org/alfresco/rest/framework/tests/core/ResourceLocatorTests.java @@ -32,9 +32,11 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -51,6 +53,7 @@ import org.alfresco.rest.framework.resource.EntityResource; import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.tests.api.mocks.Farmer; import org.alfresco.rest.framework.tests.api.mocks.GoatEntityResource; +import org.alfresco.rest.framework.tests.api.mocks.GrassEntityResource; import org.alfresco.rest.framework.tests.api.mocks.SheepBaaaahResource; import org.alfresco.rest.framework.tests.api.mocks.SheepBlackSheepResource; import org.alfresco.rest.framework.tests.api.mocks.SheepEntityResource; @@ -148,7 +151,30 @@ public class ResourceLocatorTests collResource = locator.locateResource(api,templateVars, HttpMethod.GET); } - + + @Test + public void testLocateActions() + { + Map templateVars = new HashMap(); + ResourceWithMetadata collResource = null; + templateVars.put(ResourceLocator.COLLECTION_RESOURCE, "grass"); + templateVars.put(ResourceLocator.ENTITY_ID, "grassId"); + templateVars.put(ResourceLocator.RELATIONSHIP_RESOURCE, "cut"); + try + { + collResource = locator.locateResource(api, templateVars, HttpMethod.GET); + fail("Should throw an UnsupportedResourceOperationException"); + } + catch (UnsupportedResourceOperationException error) + { + //this is correct + } + + collResource = locator.locateResource(api, templateVars, HttpMethod.POST); + assertEquals(GrassEntityResource.class, collResource.getResource().getClass()); + assertEquals(ResourceMetadata.RESOURCE_TYPE.ACTION, collResource.getMetaData().getType()); + } + @Test public void testLocateProperties() {