diff --git a/source/java/org/alfresco/rest/framework/core/ResourceInspector.java b/source/java/org/alfresco/rest/framework/core/ResourceInspector.java index 48db76e2fb..138d74f91b 100644 --- a/source/java/org/alfresco/rest/framework/core/ResourceInspector.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceInspector.java @@ -362,7 +362,10 @@ public class ResourceInspector 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)); + if (dType!= null) + { + params.add(ResourceParameter.valueOf(dType.getSimpleName().toUpperCase(), "The entity", "Unique entity properties", true, KIND.HTTP_BODY_OBJECT, true, dType)); + } } @@ -532,11 +535,11 @@ public class ResourceInspector { if (isDeleted(opera.getValue().getSecond())) { - metainfo.add(new ResourceMetadata(opera.getKey(), RESOURCE_TYPE.ACTION, null, api, new HashSet(Arrays.asList(opera.getValue().getFirst())), null)); + metainfo.add(new ActionResourceMetaData(opera.getKey(), api, new HashSet(Arrays.asList(opera.getValue().getFirst())))); } else { - metainfo.add(new ResourceMetadata(opera.getKey(), RESOURCE_TYPE.ACTION, Arrays.asList(opera.getValue().getFirst()), api, null, null)); + metainfo.add(new ActionResourceMetaData(opera.getKey(), Arrays.asList(opera.getValue().getFirst()), api, opera.getValue().getSecond())); } } } diff --git a/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java b/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java index a8847ea1f8..4442f1ad88 100755 --- a/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceInspectorUtil.java @@ -6,6 +6,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import org.alfresco.rest.framework.Action; +import org.alfresco.rest.framework.resource.parameters.Parameters; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.BridgeMethodResolver; @@ -31,12 +33,50 @@ public class ResourceInspectorUtil protected static Class determineType(Class resource, Method method) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); - Class returnType = GenericTypeResolver.resolveReturnType(resolvedMethod, resource); - if (List.class.isAssignableFrom(returnType)) + + /* + * 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 + * then it doesn't have to match. So we need special logic for actions + */ + Annotation annot = AnnotationUtils.findAnnotation(resolvedMethod, Action.class); + if (annot != null) { - return GenericCollectionTypeResolver.getCollectionReturnType(method); + return determinActionType(resource, method); + } + else + { + Class returnType = GenericTypeResolver.resolveReturnType(resolvedMethod, resource); + if (List.class.isAssignableFrom(returnType)) + { + return GenericCollectionTypeResolver.getCollectionReturnType(method); + } + return returnType; + } + } + + protected static Class determinActionType(Class resource, Method method) + { + //Its an Action annotated method and its a bit special + Class[] paramTypes = method.getParameterTypes(); + + if (!String.class.equals(paramTypes[0]) || !Parameters.class.equals(paramTypes[paramTypes.length-1])) + { + throw new IllegalArgumentException("An action method signature must start with a String uniqueId and end with the 'Parameters' object "); + } + if (paramTypes.length == 2) + { + //No parameter required + return null; + } + else if (paramTypes.length == 3) + { + return paramTypes[1]; // Return the middle parameter + } + else + { + throw new IllegalArgumentException("An action method signature should have 3 parameters (uniqueId, typePassedin, Parameters)"); } - return returnType; } /** @@ -71,6 +111,16 @@ public class ResourceInspectorUtil return annotatedMethods; } + /** + * Invokes a no arg method and returns the result + * @param annotatedMethod Method + * @param obj Object + * @return result of method call + */ + public static Object invokeMethod(Method annotatedMethod, Object obj) + { + return invokeMethod(annotatedMethod, obj, null); + } /** * Invokes a method and returns the result @@ -78,13 +128,13 @@ public class ResourceInspectorUtil * @param obj Object * @return result of method call */ - public static Object invokeMethod(Method annotatedMethod, Object obj) + public static Object invokeMethod(Method annotatedMethod, Object obj, Object... args) { if (annotatedMethod != null) { try { - return annotatedMethod.invoke(obj, null); + return annotatedMethod.invoke(obj, args); } catch (IllegalArgumentException error) { diff --git a/source/test-java/org/alfresco/rest/framework/tests/api/mocks/Grass.java b/source/test-java/org/alfresco/rest/framework/tests/api/mocks/Grass.java index a11501ba5e..1a69b94535 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/api/mocks/Grass.java +++ b/source/test-java/org/alfresco/rest/framework/tests/api/mocks/Grass.java @@ -11,6 +11,11 @@ public class Grass final private String id; private String color = "green"; + public Grass() + { + this.id = null; + } + public Grass(String id) { super(); 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 764d1aa343..337dbaab19 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 @@ -3,6 +3,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.core.ResourceParameter; import org.alfresco.rest.framework.resource.EntityResource; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; import org.alfresco.rest.framework.resource.parameters.Parameters; @@ -19,13 +20,13 @@ public class GrassEntityResource implements EntityResourceAction.ReadById } @Action("cut") - @WebApiDescription(title = "Cuts the grass") public String cutLawn(String id, Parameters parameters) { return "All done"; } @Action("grow") @WebApiDescription(title = "Grow the grass") + @WebApiParam(name = "Grass", title = "The grass.",required=true, kind = ResourceParameter.KIND.HTTP_BODY_OBJECT) 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 193368f158..585648f702 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 @@ -18,6 +18,7 @@ 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.ActionResourceMetaData; import org.alfresco.rest.framework.core.ResourceInspector; import org.alfresco.rest.framework.core.ResourceInspectorUtil; import org.alfresco.rest.framework.core.ResourceMetadata; @@ -26,6 +27,7 @@ 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.resource.parameters.Params; 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; @@ -352,7 +354,7 @@ public class InspectorTests ResourceOperation op = ResourceInspector.inspectOperation(FlockEntityResource.class, aMethod, HttpMethod.PUT); assertNotNull(op); List params = op.getParameters(); - assertNotNull(params); + assertTrue(params.size()==2); for (ResourceParameter param:params) { if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) @@ -365,7 +367,7 @@ public class InspectorTests op = ResourceInspector.inspectOperation(SheepBlackSheepResource.class, aMethod, HttpMethod.POST); assertNotNull(op); params = op.getParameters(); - assertNotNull(params); + assertTrue(params.size()==2); for (ResourceParameter param:params) { if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) @@ -389,29 +391,44 @@ public class InspectorTests } @Test - public void testInspectActions() + public void testInspectActions() throws IllegalAccessException, InstantiationException { Api api = Api.valueOf("alfrescomock", "private", "1"); List metainfo = new ArrayList(); + GrassEntityResource grassEntityResource = new GrassEntityResource(); ResourceInspector.inspectActions(api, GrassEntityResource.class,"-root-", metainfo); assertTrue(metainfo.size()==2); + for (ResourceMetadata resourceMetadata : metainfo) { assertEquals(ResourceMetadata.RESOURCE_TYPE.ACTION, resourceMetadata.getType()); + ActionResourceMetaData actionResourceMetaData = (ActionResourceMetaData) resourceMetadata; + Method actionMethod = actionResourceMetaData.getActionMethod(); + String result = null; 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)); + Class paramType = resourceMetadata.getObjectType(HttpMethod.POST); + Object paramObj = paramType.newInstance(); + result = (String) ResourceInspectorUtil.invokeMethod(actionMethod,grassEntityResource, "xyz", paramObj, Params.valueOf("notUsed", null)); + assertEquals("Growing well",result); break; case "/-root-/{entityId}/cut": assertTrue("GrassEntityResource supports POST", resourceMetadata.supports(HttpMethod.POST)); assertFalse("GrassEntityResource does not support GET", resourceMetadata.supports(HttpMethod.GET)); + assertNull(resourceMetadata.getObjectType(HttpMethod.POST)); + result = (String) ResourceInspectorUtil.invokeMethod(actionMethod,grassEntityResource, "xyz", Params.valueOf("notUsed", null)); + assertEquals("All done",result); break; default: fail("Invalid action information."); } + + + } }