This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 5de3c32d619696ee65152c983ead7079fd487e62
Author: Andi Huber <ahu...@apache.org>
AuthorDate: Thu Apr 30 07:07:03 2020 +0200

    ISIS-2340: share logic of ObjectAdapterAccessHelper/UpdateHelper (14)
---
 .../spec/interaction/ActionInteraction.java        | 149 ++++++++++++++++++---
 .../spec/interaction/InteractionVeto.java          |   7 +-
 .../metamodel/spec/interaction/ManagedAction.java  |  18 +++
 .../spec/interaction/MemberInteraction.java        |  17 ++-
 .../domainobjects/ObjectAndActionInvocation.java   |  18 +++
 .../rendering/service/RepresentationService.java   |   5 +-
 .../RepresentationServiceContentNegotiator.java    |   4 +-
 .../resources/DomainObjectResourceServerside.java  |  18 +--
 .../viewer/resources/DomainResourceHelper.java     |  86 ++++++------
 .../resources/InteractionFailureHandler.java       |  86 ++++++------
 .../viewer/resources/ObjectActionArgHelper.java    |   2 +-
 11 files changed, 283 insertions(+), 127 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionInteraction.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionInteraction.java
index 9fa7c6a..9d4e2ee 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionInteraction.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionInteraction.java
@@ -18,12 +18,23 @@
  */
 package org.apache.isis.core.metamodel.spec.interaction;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import org.apache.isis.core.commons.collections.Can;
 import org.apache.isis.core.commons.internal.base._Either;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.consent.Veto;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import 
org.apache.isis.core.metamodel.spec.interaction.ManagedMember.MemberType;
 
+import lombok.Data;
 import lombok.NonNull;
+import lombok.Value;
 import lombok.val;
 
 public final class ActionInteraction extends MemberInteraction<ManagedAction, 
ActionInteraction> {
@@ -34,33 +45,40 @@ public final class ActionInteraction extends 
MemberInteraction<ManagedAction, Ac
         SAFE
     }
     
+    @Value(staticConstructor = "of")
+    public static class Result {
+        private final ManagedAction managedAction;
+        private final List<ManagedObject> parameterList;
+        private final ManagedObject actionReturnedObject;
+    }
+
     public static final ActionInteraction start(
             @NonNull final ManagedObject owner,
             @NonNull final String memberId) {
-    
+
         val managedAction = ManagedAction.lookupAction(owner, memberId);
-        
+
         final _Either<ManagedAction, InteractionVeto> chain = 
managedAction.isPresent()
                 ? _Either.left(managedAction.get())
                 : _Either.right(InteractionVeto.notFound(MemberType.ACTION, 
memberId));
-                
+
         return new ActionInteraction(chain);
     }
-    
+
     ActionInteraction(@NonNull _Either<ManagedAction, InteractionVeto> chain) {
         super(chain);
     }
 
     public ActionInteraction checkSemanticConstraint(@NonNull 
SemanticConstraint semanticConstraint) {
-        
+
         chain = chain.leftRemap(action->{
-            
+
             val actionSemantics = action.getAction().getSemantics();
-            
+
             switch(semanticConstraint) {
             case NONE:
                 return _Either.left(action);
-            
+
             case IDEMPOTENT:
                 return (! actionSemantics.isIdempotentInNature()) 
                         ? 
_Either.right(InteractionVeto.actionNotIdempotent(action)) 
@@ -72,24 +90,113 @@ public final class ActionInteraction extends 
MemberInteraction<ManagedAction, Ac
             default:
                 throw _Exceptions.unmatchedCase(semanticConstraint); // 
unexpected code reach
             }
-             
+
         });
-        
+
         return this;
     }
 
+    @Value(staticConstructor = "of")
+    public static class ManagedParameter {
+        @NonNull private final ManagedAction owningAction;
+        @NonNull private final ObjectActionParameter parameter;
+        @NonNull private final ManagedObject value;
+        public ManagedObject getOwningObject() {
+            return getOwningAction().getOwner();
+        }
+        //TODO unchecked length
+        public static Can<ManagedParameter> listOf(ManagedAction owningAction, 
List<ManagedObject> paramValueList) {
+            val paramValueIterator = paramValueList.iterator();
+            return owningAction.getAction().getParameters()
+            .map(param->{
+                final ManagedObject paramValue = Optional
+                        .ofNullable(paramValueIterator.next())
+                        .orElse(ManagedObject.of(param.getSpecification(), 
null));
+                return ManagedParameter.of(owningAction, param, paramValue);
+            });
+        }
+        public Optional<InteractionVeto> validate() {
+            return Optional.ofNullable(
+                getParameter()
+                    .isValid(getOwningObject(), getValue(), 
InteractionInitiatedBy.USER))
+            .map(reasonNotValid->InteractionVeto.actionParamInvalid(new 
Veto(reasonNotValid)));
+        }
+    }
+    
+    public static interface ParameterInvalidCallback {
+        void onParameterInvalid(ManagedParameter managedParameter, 
InteractionVeto veto);
+    }
+    
+    
+    public ActionInteraction useParameters(
+            @NonNull final Function<ManagedAction, List<ManagedObject>> 
actionParameterProvider, 
+            final ParameterInvalidCallback parameterInvalidCallback) {
 
-//    public PropertyHandle modifyProperty(
-//            @NonNull final Function<ManagedProperty, ManagedObject> 
newProperyValueProvider) {
-//
-//        chain = chain.leftRemap(property->{
-//            val validityVeto = 
property.modifyProperty(newProperyValueProvider.apply(property));
-//            return validityVeto.isPresent()
-//                ? _Either.right(validityVeto.get()) 
-//                : _Either.left(property); 
-//        });
-//        return this;
-//    }
+        chain = chain.leftRemap(action->{
+            
+            state.setParameterList(actionParameterProvider.apply(action));
+            
+            val managedParameters = ManagedParameter.listOf(action, 
state.getParameterList());
+            
+            boolean invalid = false;
+            for(val managedParameter : managedParameters) {
+                // validate each individual argument
+                val veto = managedParameter.validate();
+                if(veto.isPresent()) {
+                    invalid = true;
+                    if(parameterInvalidCallback!=null) {
+                        
parameterInvalidCallback.onParameterInvalid(managedParameter, veto.get());
+                    }
+                }
+            }
+            
+            if(invalid) {
+                //TODO veto
+            }
+            
+            // validate entire param-list
+            val validityVeto = action.getAction()
+                    .isArgumentSetValid(action.getOwner(), 
state.getParameterList(), InteractionInitiatedBy.USER);
+            return validityVeto.isVetoed()
+                    ? 
_Either.right(InteractionVeto.actionParamInvalid(validityVeto)) 
+                    : _Either.left(action); 
+                    
+        });
+        return this;
+    }
+
+    public <X extends Throwable> 
+    Result getResultElseThrow(Function<InteractionVeto, ? extends X> 
onFailure) throws X {
+        
+        chain = chain.leftRemap(action->{
+            val actionResultOrVeto = action.invoke(state.getParameterList());
+            
+            if(actionResultOrVeto.isLeft()) {
+                val actionResult = actionResultOrVeto.leftIfAny();
+                state.setInteractionResult(Result.of(action, 
state.getParameterList(), actionResult));
+                return _Either.left(action);
+            } else {
+                return _Either.right(actionResultOrVeto.rightIfAny());
+            }
+            
+        });
+        
+        if (chain.isLeft()) {
+            return state.getInteractionResult();
+        } else {
+            throw onFailure.apply(chain.rightIfAny());
+        }
+    }
+    
+    // -- HELPER
+    
+    private final State state = new State();
+    @Data
+    private static class State {
+        @NonNull private List<ManagedObject> parameterList = 
Collections.emptyList();
+        private Result interactionResult;
+    }
 
     
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
index e323bbb..547afc5 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
@@ -45,7 +45,8 @@ public class InteractionVeto implements Serializable {
         INVALID, 
         
         ACTION_NOT_SAFE,
-        ACTION_NOT_IDEMPOTENT,
+        ACTION_NOT_IDEMPOTENT, 
+        ACTION_PARAM_INVALID,
     }
     
     @NonNull private final VetoType vetoType;
@@ -83,6 +84,10 @@ public class InteractionVeto implements Serializable {
                 action.getId());
         return of(VetoType.ACTION_NOT_IDEMPOTENT, new Veto(reason));
     }
+    
+    public static InteractionVeto actionParamInvalid(@NonNull Consent 
vetoConsent) {
+        return of(VetoType.ACTION_PARAM_INVALID, vetoConsent);
+    }
 
     public String getReason() {
         return getVetoConsent().getReason();
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
index a849770..434fb95 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
@@ -18,13 +18,17 @@
  */
 package org.apache.isis.core.metamodel.spec.interaction;
 
+import java.util.List;
 import java.util.Optional;
 
+import org.apache.isis.core.commons.internal.base._Either;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.val;
 
 public final class ManagedAction extends ManagedMember {
 
@@ -65,6 +69,20 @@ public final class ManagedAction extends ManagedMember {
     public MemberType getMemberType() {
         return MemberType.ACTION;
     }
+    
+    // -- INTERACTION
+    
+    public _Either<ManagedObject, InteractionVeto> invoke(@NonNull 
List<ManagedObject> actionParameters) {
+            
+        //TODO validate params, and handle invocation exceptions
+        
+        final ManagedObject mixedInAdapter = null; // filled in automatically ?
+        val actionResult = getAction()
+                .execute(getOwner(), mixedInAdapter , actionParameters, 
InteractionInitiatedBy.USER);
+        
+        return _Either.left(actionResult);
+        
+    }
 
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberInteraction.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberInteraction.java
index 242a614..8612cbb 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberInteraction.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberInteraction.java
@@ -19,7 +19,6 @@
 package org.apache.isis.core.metamodel.spec.interaction;
 
 import java.util.Optional;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 import org.apache.isis.applib.annotation.Where;
@@ -77,13 +76,18 @@ public abstract class MemberInteraction<T extends 
ManagedMember, H extends Membe
         }
         return _Casts.uncheckedCast(this);
     }
-    
-    @Deprecated
-    public H peekOnFailure(Consumer<InteractionVeto> onFailure) {
-        chain.right().ifPresent(onFailure);
-        return _Casts.uncheckedCast(this);
+
+    public <X extends Throwable> 
+    H validateElseThrow(Function<InteractionVeto, ? extends X> onFailure) 
throws X {
+        val veto = chain.rightIfAny();
+        if (veto == null) {
+            return _Casts.uncheckedCast(this);
+        } else {
+            throw onFailure.apply(veto);
+        }
     }
 
+    @Deprecated // use more specialized methods 
     public <X extends Throwable> 
     T getOrElseThrow(Function<InteractionVeto, ? extends X> onFailure) throws 
X {
         val value = chain.leftIfAny();
@@ -94,6 +98,7 @@ public abstract class MemberInteraction<T extends 
ManagedMember, H extends Membe
         }
     }
     
+    @Deprecated 
     public Optional<T> get() {
         return chain.left();
     }
diff --git 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
index 07d0ba1..f541a6e 100644
--- 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
+++ 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
@@ -25,11 +25,27 @@ import 
org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.interaction.ActionInteraction;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import 
org.apache.isis.viewer.restfulobjects.applib.domainobjects.ActionResultRepresentation;
 
+import lombok.NonNull;
+
 public class ObjectAndActionInvocation {
 
+    public static ObjectAndActionInvocation of(
+            @NonNull ActionInteraction.Result actionInteractionResult, 
+            @NonNull JsonRepresentation argsJsonRepr,
+            @NonNull ActionResultReprRenderer.SelfLink selfLink) {
+        return new ObjectAndActionInvocation(
+                actionInteractionResult.getManagedAction().getOwner(), 
+                actionInteractionResult.getManagedAction().getAction(),
+                argsJsonRepr,
+                actionInteractionResult.getParameterList(),
+                actionInteractionResult.getActionReturnedObject(), 
+                selfLink);
+    }
+    
     private final ManagedObject objectAdapter;
     private final ObjectAction action;
     private final JsonRepresentation arguments;
@@ -103,4 +119,6 @@ public class ObjectAndActionInvocation {
         return ActionResultRepresentation.ResultType.DOMAIN_OBJECT;
     }
 
+
+
 }
\ No newline at end of file
diff --git 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationService.java
 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationService.java
index 9462088..4f6d777 100644
--- 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationService.java
+++ 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationService.java
@@ -25,7 +25,6 @@ import 
org.apache.isis.core.metamodel.spec.interaction.ManagedAction;
 import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection;
 import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty;
 import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
-import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ActionResultReprRenderer;
 import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndActionInvocation;
 
 /**
@@ -75,8 +74,6 @@ public interface RepresentationService {
 
     Response actionResult(
             IResourceContext resourceContext,
-            ObjectAndActionInvocation objectAndActionInvocation,
-            ActionResultReprRenderer.SelfLink selfLink);
-    
+            ObjectAndActionInvocation objectAndActionInvocation);
 
 }
diff --git 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationServiceContentNegotiator.java
 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationServiceContentNegotiator.java
index 53434ee..bd0b518 100644
--- 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationServiceContentNegotiator.java
+++ 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/RepresentationServiceContentNegotiator.java
@@ -37,7 +37,6 @@ import 
org.apache.isis.core.metamodel.spec.interaction.ManagedAction;
 import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection;
 import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty;
 import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
-import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ActionResultReprRenderer.SelfLink;
 import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndActionInvocation;
 import 
org.apache.isis.viewer.restfulobjects.rendering.service.conneg.ContentNegotiationService;
 import 
org.apache.isis.viewer.restfulobjects.rendering.service.conneg.ContentNegotiationServiceForRestfulObjectsV1_0;
@@ -109,8 +108,7 @@ public class RepresentationServiceContentNegotiator 
implements RepresentationSer
     @Override
     public Response actionResult(
             final IResourceContext renderContext,
-            final ObjectAndActionInvocation objectAndActionInvocation,
-            final SelfLink selfLink) {
+            final ObjectAndActionInvocation objectAndActionInvocation) {
 
         final ResponseBuilder responseBuilder = buildResponse(
                 connegService -> connegService.buildResponse(renderContext, 
objectAndActionInvocation));
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
index 506b9d2..dc8f0ee 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
@@ -460,13 +460,10 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
             
             return proposedNewValue;
         })
-        .getOrElseThrow(InteractionFailureHandler::onFailure);
+        .validateElseThrow(InteractionFailureHandler::onFailure);
 
-        val domainResourceHelper = 
DomainResourceHelper.ofObjectResource(resourceContext, objectAdapter);
-        return domainResourceHelper.propertyDetails(
-                propertyId,
-                ManagedMember.RepresentationMode.WRITE
-                );
+        return DomainResourceHelper.ofObjectResource(resourceContext, 
objectAdapter)
+                .propertyDetails(propertyId, 
ManagedMember.RepresentationMode.WRITE);
     }
 
     @Override
@@ -490,13 +487,10 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
         .checkVisibility(resourceContext.getWhere())
         .checkUsability(resourceContext.getWhere(), AccessIntent.MUTATE)
         .modifyProperty(property->null)
-        .getOrElseThrow(InteractionFailureHandler::onFailure);
+        .validateElseThrow(InteractionFailureHandler::onFailure);
 
-        val domainResourceHelper = 
DomainResourceHelper.ofObjectResource(resourceContext, objectAdapter);
-        return domainResourceHelper.propertyDetails(
-                propertyId,
-                ManagedMember.RepresentationMode.WRITE
-                );
+        return DomainResourceHelper.ofObjectResource(resourceContext, 
objectAdapter)
+                .propertyDetails(propertyId, 
ManagedMember.RepresentationMode.WRITE);
     }
 
     @Override
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
index befd24a..3dd27f0 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
@@ -22,10 +22,11 @@ import javax.ws.rs.core.Response;
 
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.xactn.TransactionService;
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.interaction.ActionInteraction;
+import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto;
+import 
org.apache.isis.core.metamodel.spec.interaction.ActionInteraction.ManagedParameter;
 import 
org.apache.isis.core.metamodel.spec.interaction.ActionInteraction.SemanticConstraint;
-import org.apache.isis.core.metamodel.spec.interaction.ManagedAction;
 import org.apache.isis.core.metamodel.spec.interaction.ManagedMember;
 import 
org.apache.isis.core.metamodel.spec.interaction.MemberInteraction.AccessIntent;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
@@ -38,6 +39,7 @@ import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndAc
 import 
org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService;
 import org.apache.isis.viewer.restfulobjects.viewer.context.ResourceContext;
 
+import lombok.NonNull;
 import lombok.val;
 
 class DomainResourceHelper {
@@ -155,11 +157,9 @@ class DomainResourceHelper {
      */
     public Response invokeActionQueryOnly(final String actionId, final 
JsonRepresentation arguments) {
 
-        val action = ObjectAdapterAccessHelper.of(resourceContext, 
objectAdapter)
-                .getObjectActionThatIsVisibleForIntentAndSemanticConstraint(
-                        actionId, AccessIntent.MUTATE, 
SemanticConstraint.SAFE);
-
-        return invokeActionUsingAdapters(action, arguments, 
ActionResultReprRenderer.SelfLink.INCLUDED);
+        return invokeAction( 
+                actionId, AccessIntent.MUTATE, SemanticConstraint.SAFE, 
+                arguments, ActionResultReprRenderer.SelfLink.EXCLUDED);
     }
 
     /**
@@ -174,11 +174,9 @@ class DomainResourceHelper {
      */
     public Response invokeActionIdempotent(final String actionId, final 
JsonRepresentation arguments) {
 
-        val action = ObjectAdapterAccessHelper.of(resourceContext, 
objectAdapter)
-                .getObjectActionThatIsVisibleForIntentAndSemanticConstraint(
-                        actionId, AccessIntent.MUTATE, 
SemanticConstraint.IDEMPOTENT);
-
-        return invokeActionUsingAdapters(action, arguments, 
ActionResultReprRenderer.SelfLink.EXCLUDED);
+        return invokeAction( 
+                actionId, AccessIntent.MUTATE, SemanticConstraint.IDEMPOTENT, 
+                arguments, ActionResultReprRenderer.SelfLink.EXCLUDED);
     }
 
     /**
@@ -188,42 +186,50 @@ class DomainResourceHelper {
      */
     public Response invokeAction(final String actionId, final 
JsonRepresentation arguments) {
 
-        val action = ObjectAdapterAccessHelper.of(resourceContext, 
objectAdapter)
-                .getObjectActionThatIsVisibleForIntentAndSemanticConstraint(
-                        actionId, AccessIntent.MUTATE, 
SemanticConstraint.NONE);
-
-        return invokeActionUsingAdapters(action, arguments, 
ActionResultReprRenderer.SelfLink.EXCLUDED);
+        return invokeAction( 
+                actionId, AccessIntent.MUTATE, SemanticConstraint.NONE, 
+                arguments, ActionResultReprRenderer.SelfLink.EXCLUDED);
     }
 
-
-    private Response invokeActionUsingAdapters(
-            final ManagedAction managedAction,
-            final JsonRepresentation arguments,
-            final ActionResultReprRenderer.SelfLink selfLink) {
-
-        val action = managedAction.getAction();
-        val objectAdapter = this.objectAdapter;
-        val argHelper = new ObjectActionArgHelper(resourceContext, 
managedAction);
-        val argAdapters = argHelper.parseAndValidateArguments(arguments);
-
+    private Response invokeAction(
+            @NonNull final String actionId, 
+            @NonNull final AccessIntent intent,
+            @NonNull final SemanticConstraint semanticConstraint,
+            @NonNull final JsonRepresentation arguments,
+            @NonNull final ActionResultReprRenderer.SelfLink selfLink) {
+        
+        val where = resourceContext.getWhere();
+        
+        val actionInteraction = ActionInteraction.start(objectAdapter, 
actionId)
+        .checkVisibility(where)
+        .checkUsability(where, intent)
+        .checkSemanticConstraint(semanticConstraint)
+        .useParameters(action->{
+            
+            val argAdapters = ObjectActionArgHelper.of(resourceContext, action)
+                    .parseAndValidateArguments(arguments);
+            
+            return argAdapters;
+            
+        }, 
+                (ManagedParameter managedParameter, InteractionVeto veto)->{
+                    
InteractionFailureHandler.onParameterInvalid(managedParameter, veto, arguments);
+                }
+        );
+        
         if(resourceContext.isValidateOnly()) {
-            // nothing more to do.
-            // if there had been a validation error, then an exception would 
have been thrown above.
+            
actionInteraction.validateElseThrow(InteractionFailureHandler::onFailure);
             return Response.noContent().build();
         }
-
-        // invoke
-        final ManagedObject mixedInAdapter = null; // action will 
automatically fill in if a mixin
-        final ManagedObject returnedAdapter = action.execute(
-                objectAdapter,  mixedInAdapter, argAdapters,
-                InteractionInitiatedBy.USER);
-
-        final ObjectAndActionInvocation objectAndActionInvocation =
-                new ObjectAndActionInvocation(objectAdapter, action, 
arguments, argAdapters, returnedAdapter, selfLink);
+        
+        val actionInteractionResult = actionInteraction
+                .getResultElseThrow(InteractionFailureHandler::onFailure);
+        
+        val objectAndActionInvocation = 
ObjectAndActionInvocation.of(actionInteractionResult, arguments, selfLink);
 
         // response
         transactionService.flushTransaction();
-        return representationService.actionResult(resourceContext, 
objectAndActionInvocation, selfLink);
+        return representationService.actionResult(resourceContext, 
objectAndActionInvocation);
     }
 
 
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
index e12199b..0759dfc 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
@@ -20,68 +20,76 @@ package 
org.apache.isis.viewer.restfulobjects.viewer.resources;
 
 import javax.annotation.Nullable;
 
+import 
org.apache.isis.core.metamodel.spec.interaction.ActionInteraction.ManagedParameter;
 import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.RestfulResponse;
 import 
org.apache.isis.viewer.restfulobjects.rendering.RestfulObjectsApplicationException;
 
+import lombok.val;
+
 public class InteractionFailureHandler {
 
-    public static RestfulObjectsApplicationException onFailure(@Nullable final 
InteractionVeto failure) {
-        
-        if(failure==null) {
+    public static RestfulObjectsApplicationException onFailure(
+            @Nullable final InteractionVeto veto) {
+
+        if(veto==null) {
             return RestfulObjectsApplicationException
                     
.createWithMessage(RestfulResponse.HttpStatusCode.INTERNAL_SERVER_ERROR,
                             "unexpected empty failure holder");
         }
-        
-        switch(failure.getVetoType()) {
+
+        switch(veto.getVetoType()) {
         case NOT_FOUND:
         case HIDDEN:
             return RestfulObjectsApplicationException
-            .createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND,
-                    failure.getReason());
+                    
.createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND,
+                            veto.getReason());
+
         case READONLY:
         case INVALID:
             return RestfulObjectsApplicationException
-            .createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN,
-                    failure.getReason());
-            
+                    
.createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN,
+                            veto.getReason());
+
         case ACTION_NOT_SAFE:
         case ACTION_NOT_IDEMPOTENT:
             return RestfulObjectsApplicationException
-            
.createWithMessage(RestfulResponse.HttpStatusCode.METHOD_NOT_ALLOWED,
-                    failure.getReason());
+                    
.createWithMessage(RestfulResponse.HttpStatusCode.METHOD_NOT_ALLOWED,
+                            veto.getReason());
+
+        case ACTION_PARAM_INVALID:
+            return RestfulObjectsApplicationException
+                    
.createWithMessage(RestfulResponse.HttpStatusCode.VALIDATION_FAILED,
+                            veto.getReason());
         }
-        
+
         return RestfulObjectsApplicationException
                 
.createWithMessage(RestfulResponse.HttpStatusCode.INTERNAL_SERVER_ERROR,
-                        "unmatched veto type " + failure.getVetoType());
-        
+                        "unmatched veto type " + veto.getVetoType());
+
     }
-    
-    
-//    public static void onFailure(@Nullable final InteractionVeto failure) {
-//        
-//        if(failure==null) {
-//            return;
-//        }
-//        
-//        switch(failure.getVetoType()) {
-//        case NOT_FOUND:
-//        case HIDDEN:
-//            throw RestfulObjectsApplicationException
-//            .createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND,
-//                    failure.getReason());
-//        case READONLY:
-//        case INVALID:
-//            throw RestfulObjectsApplicationException
-//            .createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN,
-//                    failure.getReason());
-//        }
-//        
-//    }
-        
-        
 
+    public static RestfulObjectsApplicationException onParameterListInvalid(
+            @Nullable final InteractionVeto veto, 
+            @Nullable final JsonRepresentation arguments) {
+
+        arguments.mapPut("x-ro-invalidReason", veto.getReason());
+        return RestfulObjectsApplicationException
+                
.createWithBody(RestfulResponse.HttpStatusCode.VALIDATION_FAILED,
+                        arguments,
+                        "Validation failed, see body for details");
+    }
     
+    // collect info for each individual param that is not valid
+    public static void onParameterInvalid(
+            @Nullable final ManagedParameter managedParameter, 
+            @Nullable final InteractionVeto veto, 
+            @Nullable final JsonRepresentation arguments) {
+
+        val paramId = managedParameter.getParameter().getId();
+        val argRepr = arguments.getRepresentation(paramId);
+        argRepr.mapPut("invalidReason", veto.getReason());
+    }
+
 }
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectActionArgHelper.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectActionArgHelper.java
index 9929406..50e1638 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectActionArgHelper.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectActionArgHelper.java
@@ -41,7 +41,7 @@ import lombok.val;
  * Utility class that encapsulates the logic for parsing arguments to be 
invoked by an
  * {@link ObjectAction}.
  */
-@RequiredArgsConstructor
+@RequiredArgsConstructor(staticName = "of")
 public class ObjectActionArgHelper {
 
     private final IResourceContext resourceContext;

Reply via email to