You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/10/03 15:20:26 UTC

[isis] 01/01: ISIS-2871: evaluate action parameter defaults honoring multiselect providers (if any)

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

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

commit 579898050b15abccf9e5c44a77f0456dae2cb48d
Author: andi-huber <ah...@apache.org>
AuthorDate: Sun Oct 3 17:20:11 2021 +0200

    ISIS-2871: evaluate action parameter defaults honoring multiselect
    providers (if any)
---
 .../interactions/managed/ActionInteraction.java    | 11 ++++-
 .../managed/ActionInteractionHead.java             | 50 +++++++++++++--------
 .../interactions/managed/ManagedAction.java        | 20 +++++++--
 .../interactions/managed/MultiselectChoices.java   | 29 ++++++++++++
 .../managed/ParameterNegotiationModel.java         | 18 ++++++--
 .../managed/nonscalar/DataTableModel.java          | 16 ++++---
 ...meterDefaultsFacetFromAssociatedCollection.java |  6 +--
 .../core/metamodel/spec/feature/ObjectAction.java  | 11 ++---
 .../spec/feature/ObjectActionParameter.java        |  9 ----
 .../specloader/specimpl/ObjectActionDefault.java   | 14 +-----
 .../specloader/specimpl/ObjectActionMixedIn.java   |  6 ---
 .../testdomain/interact/ActionInteractionTest.java | 18 ++++----
 .../interact/CollectionInteractionTest.java        |  7 ++-
 .../testdomain/interact/NewParameterModelTest.java | 10 ++---
 .../interaction/DomainObjectTesterFactory.java     | 40 ++++++++++++++---
 .../util/interaction/InteractionTestAbstract.java  | 31 +++++--------
 .../domainobjects/ObjectActionReprRenderer.java    | 52 ++++++++++------------
 17 files changed, 202 insertions(+), 146 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java
index 4130ad6..5ac8f08 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java
@@ -55,7 +55,16 @@ public final class ActionInteraction extends MemberInteraction<ManagedAction, Ac
             final @NonNull String memberId,
             final @NonNull Where where) {
 
-        val managedAction = ManagedAction.lookupAction(owner, memberId, where);
+        return startWithMultiselect(owner, memberId, where, Can::empty);
+    }
+
+    public static final ActionInteraction startWithMultiselect(
+            final @NonNull ManagedObject owner,
+            final @NonNull String memberId,
+            final @NonNull Where where,
+            final @NonNull MultiselectChoices multiselectChoices) {
+
+        val managedAction = ManagedAction.lookupActionWithMultiselect(owner, memberId, where, multiselectChoices);
 
         final _Either<ManagedAction, InteractionVeto> chain = managedAction.isPresent()
                 ? _Either.left(managedAction.get())
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteractionHead.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteractionHead.java
index 19b9c2f..d2d5915 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteractionHead.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteractionHead.java
@@ -43,20 +43,31 @@ implements HasMetaModel<ObjectAction> {
 
     @Getter(onMethod = @__(@Override))
     @NonNull private final ObjectAction metaModel;
+    @Getter private final MultiselectChoices multiselectChoices;
 
     public static ActionInteractionHead of(
-            @NonNull final ObjectAction objectAction,
-            @NonNull final ManagedObject owner,
-            @NonNull final ManagedObject target) {
-        return new ActionInteractionHead(objectAction, owner, target);
+            final @NonNull ObjectAction objectAction,
+            final @NonNull ManagedObject owner,
+            final @NonNull ManagedObject target) {
+        return new ActionInteractionHead(objectAction, owner, target, Can::empty);
+    }
+
+    public static ActionInteractionHead of(
+            final @NonNull ObjectAction objectAction,
+            final @NonNull ManagedObject owner,
+            final @NonNull ManagedObject target,
+            final @NonNull MultiselectChoices multiselectChoices) {
+        return new ActionInteractionHead(objectAction, owner, target, multiselectChoices);
     }
 
     protected ActionInteractionHead(
-            @NonNull final ObjectAction objectAction,
-            @NonNull final ManagedObject owner,
-            @NonNull final ManagedObject target) {
+            final @NonNull ObjectAction objectAction,
+            final @NonNull ManagedObject owner,
+            final @NonNull ManagedObject target,
+            final @NonNull MultiselectChoices multiselectChoices) {
         super(owner, target);
         this.metaModel = objectAction;
+        this.multiselectChoices = multiselectChoices;
     }
 
     /**
@@ -93,13 +104,8 @@ implements HasMetaModel<ObjectAction> {
             ManagedObject.of(objectActionParameter.getElementType(), argPojo));
     }
 
-    public ParameterNegotiationModel model(
-            @NonNull final Can<ManagedObject> paramValues) {
-        return ParameterNegotiationModel.of(this, paramValues);
-    }
-
-    public ParameterNegotiationModel emptyModel() {
-        return ParameterNegotiationModel.of(this, getEmptyParameterValues());
+    public ParameterNegotiationModel emptyModel(final ManagedAction managedAction) {
+        return ParameterNegotiationModel.of(managedAction, getEmptyParameterValues());
     }
 
     /**
@@ -108,7 +114,7 @@ implements HasMetaModel<ObjectAction> {
      * ActionParameterNegotiation (wiki)
      * </a>
      */
-    public ParameterNegotiationModel defaults() {
+    public ParameterNegotiationModel defaults(final ManagedAction managedAction) {
 
         // first pass ... empty values
         // second pass ... proposed default values
@@ -122,7 +128,7 @@ implements HasMetaModel<ObjectAction> {
                 // vector of packed values - where each is either scalar or non-scalar
                 paramVector->{
                     //lombok 1.18.20 issue with val - should be fixed in 1.18.22
-                    final var paramNegotiationModel = model(paramVector);
+                    final var paramNegotiationModel = modelForParamValues(managedAction, paramVector);
                     return params
                             .map(param->param.getDefault(paramNegotiationModel));
                 },
@@ -133,11 +139,19 @@ implements HasMetaModel<ObjectAction> {
                     + "parameter defaults on action %s.", getMetaModel());
         }
 
-        return model(fixedPoint.fold(
+        return modelForParamValues(managedAction,
+                fixedPoint.fold(
                 left->left,
                 right->right));
     }
 
+    // -- HELPER
+
+    private ParameterNegotiationModel modelForParamValues(
+            final ManagedAction managedAction,
+            @NonNull final Can<ManagedObject> paramValues) {
+        return ParameterNegotiationModel.of(managedAction, paramValues);
+    }
 
     /**
      * Returns either a fixed point or the last iteration.
@@ -159,6 +173,4 @@ implements HasMetaModel<ObjectAction> {
         return _Either.right(t0);
     }
 
-
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java
index d6951f7..1d3f78d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java
@@ -54,7 +54,7 @@ public final class ManagedAction extends ManagedMember {
             final @NonNull ManagedObject owner,
             final @NonNull ObjectAction action,
             final @NonNull Where where) {
-        return new ManagedAction(owner, action, where);
+        return new ManagedAction(owner, action, where, Can::empty);
     }
 
     public static final Optional<ManagedAction> lookupAction(
@@ -66,20 +66,32 @@ public final class ManagedAction extends ManagedMember {
         .map(objectAction -> of(owner, objectAction, where));
     }
 
+    public static final Optional<ManagedAction> lookupActionWithMultiselect(
+            final @NonNull ManagedObject owner,
+            final @NonNull String memberId,
+            final @NonNull Where where,
+            final @NonNull MultiselectChoices multiselectChoices) {
+
+        return ManagedMember.<ObjectAction>lookup(owner, MemberType.ACTION, memberId)
+                .map(objectAction -> new ManagedAction(owner, objectAction, where, multiselectChoices));
+    }
+
     // -- IMPLEMENTATION
 
     @Getter private final ObjectAction action;
-
     @Getter private final ActionInteractionHead interactionHead;
+    @Getter private final MultiselectChoices multiselectChoices;
 
     private ManagedAction(
             final @NonNull ManagedObject owner,
             final @NonNull ObjectAction action,
-            final @NonNull Where where) {
+            final @NonNull Where where,
+            final @NonNull MultiselectChoices multiselectChoices) {
 
         super(owner, where);
         this.action = action;
         this.interactionHead = action.interactionHead(owner);
+        this.multiselectChoices = multiselectChoices;
     }
 
     /**
@@ -87,7 +99,7 @@ public final class ManagedAction extends ManagedMember {
      * parameters if any are initialized with their defaults (taking into account any supporting methods)
      */
     public ParameterNegotiationModel startParameterNegotiation() {
-        return getInteractionHead().defaults();
+        return interactionHead.defaults(this);
     }
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MultiselectChoices.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MultiselectChoices.java
new file mode 100644
index 0000000..75dd3b1
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MultiselectChoices.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.interactions.managed;
+
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+
+@FunctionalInterface
+public interface MultiselectChoices {
+
+    Can<ManagedObject> getSelected();
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ParameterNegotiationModel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ParameterNegotiationModel.java
index ecf8d56..4bf5461 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ParameterNegotiationModel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ParameterNegotiationModel.java
@@ -54,21 +54,23 @@ import lombok.val;
 public class ParameterNegotiationModel {
 
     public static ParameterNegotiationModel of(
-            final @NonNull ActionInteractionHead head,
+            final @NonNull ManagedAction managedAction,
             final @NonNull Can<ManagedObject> initialParamValues) {
-        return new ParameterNegotiationModel(head, initialParamValues);
+        return new ParameterNegotiationModel(managedAction, initialParamValues);
 
     }
 
     @Getter private final ActionInteractionHead head;
+    private final @NonNull ManagedAction managedAction;
     private final Can<ParameterModel> paramModels;
     private final _BindableAbstract<Boolean> validationFeedbackActive;
     private final LazyObservable<String> observableActionValidation;
 
     private ParameterNegotiationModel(
-            final @NonNull ActionInteractionHead head,
+            final @NonNull ManagedAction managedAction,
             final @NonNull Can<ManagedObject> initialParamValues) {
-        this.head = head;
+        this.managedAction = managedAction;
+        this.head = managedAction.getInteractionHead(); //TODO  don't memoize
         this.validationFeedbackActive = _Bindables.forValue(false);
 
         val paramNrIterator = IntStream.range(0, initialParamValues.size()).iterator();
@@ -164,6 +166,12 @@ public class ParameterNegotiationModel {
                 .isUsable(head, pendingArgValues, InteractionInitiatedBy.USER);
     }
 
+    // -- MULTI SELECT
+
+    public MultiselectChoices getMultiselectChoices() {
+        return managedAction.getMultiselectChoices();
+    }
+
     // -- RATHER INTERNAL ...
 
     /** validates all, the action and each individual parameter */
@@ -345,4 +353,6 @@ public class ParameterNegotiationModel {
 
     }
 
+
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
index f07c4b7..6537d91 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
@@ -45,6 +45,7 @@ import org.apache.isis.core.metamodel.interactions.managed.ManagedAction;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedAction.MementoForArgs;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedMember;
+import org.apache.isis.core.metamodel.interactions.managed.MultiselectChoices;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
@@ -55,7 +56,8 @@ import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.val;
 
-public class DataTableModel {
+public class DataTableModel
+implements MultiselectChoices {
 
     // -- FACTORIES
 
@@ -195,8 +197,14 @@ public class DataTableModel {
 
     // -- ASSOCIATED ACTION WITH MULTI SELECT
 
-    public ActionInteraction startAssociatedActionInteraction(final String actionId, final Where where) {
+    @Override
+    public Can<ManagedObject> getSelected() {
+        return getDataRowsSelected()
+                .getValue()
+                .map(DataRow::getRowElement);
+    }
 
+    public ActionInteraction startAssociatedActionInteraction(final String actionId, final Where where) {
         val featureId = managedMember.getIdentifier();
         if(featureId.getType().isAction()) {
             return ActionInteraction.empty(String.format("[no such associated action %s for collection %s "
@@ -204,9 +212,7 @@ public class DataTableModel {
                     actionId,
                     featureId));
         }
-
-        //FIXME[ISIS-2871] get initial defaults from this table
-        return ActionInteraction.start(managedMember.getOwner(), actionId, where);
+        return ActionInteraction.startWithMultiselect(managedMember.getOwner(), actionId, where, this);
     }
 
     // -- MEMENTO
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/collparam/ActionParameterDefaultsFacetFromAssociatedCollection.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/collparam/ActionParameterDefaultsFacetFromAssociatedCollection.java
index d605f41..df973ca 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/collparam/ActionParameterDefaultsFacetFromAssociatedCollection.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/collparam/ActionParameterDefaultsFacetFromAssociatedCollection.java
@@ -34,17 +34,13 @@ extends ActionParameterDefaultsFacetAbstract {
         return new ActionParameterDefaultsFacetFromAssociatedCollection(param);
     }
 
-    private int paramIndex;
-
     private ActionParameterDefaultsFacetFromAssociatedCollection(final ObjectActionParameter param) {
         super(param);
-        this.paramIndex = param.getNumber();
     }
 
     @Override
     public Can<ManagedObject> getDefault(@NonNull final ParameterNegotiationModel pendingArgs) {
-        //FIXME[ISIS-2871] rather then filling in all (as proof of concept), fill in only those selected
-        return pendingArgs.getObservableParamChoices(paramIndex).getValue();
+        return pendingArgs.getMultiselectChoices().getSelected();
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
index c6d3fcd..4aeae25 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
@@ -171,10 +171,10 @@ public interface ObjectAction extends ObjectMember {
             InteractionInitiatedBy interactionInitiatedBy);
 
 
-    // -- Model for Parameter Negotiation
+    // -- INTERACTION HEAD
 
-    ActionInteractionHead interactionHead(
-            @NonNull ManagedObject actionOwner);
+    @Deprecated// use ManagedAction instead
+    ActionInteractionHead interactionHead(@NonNull ManagedObject actionOwner);
 
     // -- Parameters (declarative)
 
@@ -234,11 +234,6 @@ public interface ObjectAction extends ObjectMember {
     // -- Parameters (per instance)
 
     /**
-     * Returns the defaults references/values to be used for the action.
-     */
-    Can<ManagedObject> getDefaults(ManagedObject target);
-
-    /**
      * Returns a list of possible references/values for each parameter, which
      * the user can choose from.
      */
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java
index 553e77e..1edc930 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java
@@ -140,15 +140,6 @@ extends ObjectFeature, CurrentHolder {
         return ManagedObject.of(getElementType(), null);
     }
 
-    /** default value as result of a initial param value fixed point search */
-    default ManagedObject getDefault(final ManagedObject actionOnwer) {
-        return getAction()
-                .interactionHead(actionOnwer).defaults()
-                .getParamValues()
-                .getElseFail(getNumber());
-    }
-
-
     /**
      * Whether this parameter is visible given the entered previous arguments
      * @param head
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
index 2062f01..dde552d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
@@ -144,7 +144,8 @@ implements ObjectAction {
     }
 
     @Override
-    public ActionInteractionHead interactionHead(final @NonNull ManagedObject actionOwner) {
+    public ActionInteractionHead interactionHead(
+            final @NonNull ManagedObject actionOwner) {
         return ActionInteractionHead.of(this, actionOwner, actionOwner);
     }
 
@@ -402,17 +403,6 @@ implements ObjectAction {
         return getFacetedMethod().getFacet(ActionInvocationFacet.class);
     }
 
-    // -- defaults
-
-    @Override
-    public Can<ManagedObject> getDefaults(final ManagedObject target) {
-        // use the new defaultNXxx approach for each param in turn
-        // (the reflector will have made sure both aren't installed).
-        return interactionHead(target)
-                .defaults()
-                .getParamValues();
-    }
-
     // -- choices
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
index 3c7aa55..0151122 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
@@ -136,12 +136,6 @@ implements MixedInMember {
     }
 
     @Override
-    public Can<ManagedObject> getDefaults(final ManagedObject mixedInAdapter) {
-        final ManagedObject mixinAdapter = mixinAdapterFor(mixedInAdapter);
-        return mixinAction.getDefaults(mixinAdapter);
-    }
-
-    @Override
     public CanVector<ManagedObject> getChoices(
             final ManagedObject mixedInAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/ActionInteractionTest.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/ActionInteractionTest.java
index fbf70ff..ccbe728 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/ActionInteractionTest.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/ActionInteractionTest.java
@@ -25,11 +25,6 @@ import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.collections._Lists;
@@ -44,6 +39,11 @@ import org.apache.isis.testdomain.model.interaction.InteractionDemo_multiEnum;
 import org.apache.isis.testdomain.model.interaction.InteractionDemo_multiInt;
 import org.apache.isis.testdomain.util.interaction.InteractionTestAbstract;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import lombok.val;
 
 @SpringBootTest(
@@ -206,7 +206,7 @@ class ActionInteractionTest extends InteractionTestAbstract {
                 .checkUsability();
 
         val managedAction = actionInteraction.getManagedAction().get(); // should not throw
-        val pendingArgs = managedAction.getInteractionHead().defaults();
+        val pendingArgs = managedAction.startParameterNegotiation();
 
         val expectedDefaults = Can.of(
                 new InteractionDemo_biArgEnabled(null).defaultA(null),
@@ -225,7 +225,7 @@ class ActionInteractionTest extends InteractionTestAbstract {
                 .checkUsability();
 
         val managedAction = actionInteraction.getManagedAction().get(); // should not throw
-        val pendingArgs = managedAction.getInteractionHead().defaults();
+        val pendingArgs = managedAction.startParameterNegotiation();
 
         val mixin = new InteractionDemo_multiInt(null);
         val expectedDefaults = Can.<Integer>of(
@@ -261,7 +261,7 @@ class ActionInteractionTest extends InteractionTestAbstract {
                 .checkUsability();
 
         val managedAction = actionInteraction.getManagedAction().get(); // should not throw
-        val pendingArgs = managedAction.getInteractionHead().defaults();
+        val pendingArgs = managedAction.startParameterNegotiation();
 
         val mixin = new InteractionDemo_multiEnum(null);
         val expectedDefaults = Can.<DemoEnum>of(
@@ -296,7 +296,7 @@ class ActionInteractionTest extends InteractionTestAbstract {
                 .checkUsability();
 
         val managedAction = actionInteraction.getManagedAction().get(); // should not throw
-        val pendingArgs = managedAction.getInteractionHead().defaults();
+        val pendingArgs = managedAction.startParameterNegotiation();
 
         val mixin = new InteractionDemo_biListOfString(null);
         val expectedDefaults = Can.<List<String>>of(
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/CollectionInteractionTest.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/CollectionInteractionTest.java
index e570b83..cd75256 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/CollectionInteractionTest.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/CollectionInteractionTest.java
@@ -21,13 +21,10 @@ package org.apache.isis.testdomain.interact;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
@@ -36,6 +33,8 @@ import org.apache.isis.testdomain.model.interaction.Configuration_usingInteracti
 import org.apache.isis.testdomain.model.interaction.InteractionDemo;
 import org.apache.isis.testdomain.util.interaction.InteractionTestAbstract;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
 import lombok.val;
 
 @SpringBootTest(
@@ -106,7 +105,7 @@ class CollectionInteractionTest extends InteractionTestAbstract {
 
     }
 
-    @Test @Disabled("FIXME[ISIS-2871]")
+    @Test
     void choicesFromMultiselect() {
 
         val collTester =
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/NewParameterModelTest.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/NewParameterModelTest.java
index 7acc394..ff27c92 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/NewParameterModelTest.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/NewParameterModelTest.java
@@ -24,10 +24,6 @@ import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.config.presets.IsisPresets;
@@ -41,6 +37,10 @@ import org.apache.isis.testdomain.model.interaction.InteractionNpmDemo;
 import org.apache.isis.testdomain.model.interaction.InteractionNpmDemo_biArgEnabled;
 import org.apache.isis.testdomain.util.interaction.InteractionTestAbstract;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import lombok.val;
 
 @SpringBootTest(
@@ -150,7 +150,7 @@ class NewParameterModelTest extends InteractionTestAbstract {
                 .checkUsability();
 
         val managedAction = actionInteraction.getManagedAction().get(); // should not throw
-        val pendingArgs = managedAction.getInteractionHead().defaults();
+        val pendingArgs = managedAction.startParameterNegotiation();
 
         val expectedDefaults = Can.of(
                 new InteractionDemo_biArgEnabled(null).defaultA(null),
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java
index 96b2134..09ee8f4 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.testdomain.util.interaction;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Consumer;
@@ -30,12 +31,6 @@ import org.junit.jupiter.api.function.ThrowingSupplier;
 import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Service;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.exceptions.unrecoverable.DomainModelException;
@@ -74,6 +69,12 @@ import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.testdomain.util.CollectionAssertions;
 import org.apache.isis.testing.integtestsupport.applib.validate.DomainModelValidator;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
 import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -318,6 +319,32 @@ public class DomainObjectTesterFactory {
 
         }
 
+        public Object invokeWithPojos(final List<Object> pojoArgList) {
+
+            assertExists(true);
+
+            val pojoVector = Can.ofCollection(pojoArgList);
+
+            return interactionService.callAnonymous(()->{
+
+                val pendingArgs = startParameterNegotiation(true);
+
+                pendingArgs.getParamModels()
+                        .forEach(param->{
+                            pojoVector
+                                .get(param.getParamNr())
+                                .ifPresent(pojo->param.updatePojo(__->pojo));
+                        });
+
+                //pendingArgs.validateParameterSetForParameters();
+
+                val resultOrVeto = actionInteraction.invokeWith(pendingArgs);
+                assertTrue(resultOrVeto.isLeft()); // assert action did not throw
+
+                return resultOrVeto.leftIfAny().getPojo();
+            });
+        }
+
         /**
          * circumvents rule checking
          */
@@ -446,6 +473,7 @@ public class DomainObjectTesterFactory {
                                     .orElse("no such action")));
         }
 
+
     }
 
     // -- PROPERTY TESTER
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionTestAbstract.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionTestAbstract.java
index 2c395a2..2c1fef5 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionTestAbstract.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionTestAbstract.java
@@ -29,10 +29,6 @@ import javax.inject.Inject;
 
 import org.springframework.lang.Nullable;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.iactnlayer.InteractionService;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
@@ -49,6 +45,9 @@ import org.apache.isis.testdomain.util.CollectionAssertions;
 import org.apache.isis.testdomain.util.kv.KVStoreForTesting;
 import org.apache.isis.testing.integtestsupport.applib.IsisIntegrationTestAbstract;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import lombok.val;
 
 public abstract class InteractionTestAbstract extends IsisIntegrationTestAbstract {
@@ -90,24 +89,14 @@ public abstract class InteractionTestAbstract extends IsisIntegrationTestAbstrac
 
     // -- SHORTCUTS
 
-    protected Object invokeAction(final Class<?> type, final String actionId, final @Nullable List<Object> pojoArgList) {
-        val managedAction = startActionInteractionOn(type, actionId, Where.OBJECT_FORMS)
-                .getManagedAction().get(); // should not throw
-
-        assertFalse(managedAction.checkVisibility().isPresent()); // is visible
-        assertFalse(managedAction.checkUsability().isPresent()); // can invoke
-
-        val args = managedAction.getInteractionHead()
-                .getPopulatedParameterValues(pojoArgList);
-
-        // spawns its own transactional boundary, or reuses an existing one if available
-        val either = managedAction.invoke(args);
-
-        assertTrue(either.isLeft()); // assert action did not throw
-
-        val actionResultAsPojo = either.leftIfAny().getPojo();
+    protected Object invokeAction(
+            final Class<?> type, final String actionId, final @Nullable List<Object> pojoArgList) {
 
-        return actionResultAsPojo;
+        val actTester = testerFactory.actionTester(type, actionId);
+        actTester.assertExists(true);
+        actTester.assertVisibilityIsNotVetoed();
+        actTester.assertUsabilityIsNotVetoed();
+        return actTester.invokeWithPojos(pojoArgList);
     }
 
     // -- ASSERTIONS
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
index 4e5309d..c9af1c4 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
@@ -27,11 +27,11 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedAction;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter;
+import org.apache.isis.core.metamodel.interactions.managed.ParameterNegotiationModel;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.Rel;
 import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
@@ -129,26 +129,31 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
 
     private ObjectActionReprRenderer addParameterDetails() {
         final Map<String,Object> parameters = _Maps.newLinkedHashMap();
-        for (int i = 0; i < objectMember.getParameterCount(); i++) {
-            final ObjectActionParameter param = objectMember.getParameters().getElseFail(i);
-            final Object paramDetails = paramDetails(param, getInteractionInitiatedBy());
-            parameters.put(param.getId(), paramDetails);
+        if(objectMember.getParameterCount()>0) {
+            val act = ManagedAction.of(objectAdapter, objectMember, Where.ANYWHERE);
+            val paramNeg = act.startParameterNegotiation();
+            for(val paramMod : paramNeg.getParamModels()) {
+                val paramMeta = paramMod.getMetaModel();
+                final Object paramDetails = paramDetails(paramMod, paramNeg);
+                parameters.put(paramMeta.getId(), paramDetails);
+            }
         }
         representation.mapPut("parameters", parameters);
         return this;
     }
 
-    private Object paramDetails(final ObjectActionParameter param, final InteractionInitiatedBy interactionInitiatedBy) {
+    private Object paramDetails(final ManagedParameter paramMod, final ParameterNegotiationModel paramNeg) {
+        val paramMeta = paramMod.getMetaModel();
         final JsonRepresentation paramRep = JsonRepresentation.newMap();
-        paramRep.mapPut("num", param.getNumber());
-        paramRep.mapPut("id", param.getId());
-        paramRep.mapPut("name", param.getFriendlyName(objectAdapter.asProvider()));
-        paramRep.mapPut("description", param.getDescription(objectAdapter.asProvider()));
-        final Object paramChoices = choicesFor(param, interactionInitiatedBy);
+        paramRep.mapPut("num", paramMeta.getNumber());
+        paramRep.mapPut("id", paramMeta.getId());
+        paramRep.mapPut("name", paramMeta.getFriendlyName(objectAdapter.asProvider()));
+        paramRep.mapPut("description", paramMeta.getDescription(objectAdapter.asProvider()));
+        final Object paramChoices = choicesFor(paramMod, paramNeg);
         if (paramChoices != null) {
             paramRep.mapPut("choices", paramChoices);
         }
-        final Object paramDefault = defaultFor(param);
+        final Object paramDefault = defaultFor(paramMod);
         if (paramDefault != null) {
             paramRep.mapPut("default", paramDefault);
         }
@@ -156,14 +161,10 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
     }
 
     private Object choicesFor(
-            final ObjectActionParameter param,
-            final InteractionInitiatedBy interactionInitiatedBy) {
-
-        val pendingArgs = param.getAction()
-                .interactionHead(objectAdapter)
-                .emptyModel();
-
-        val choiceAdapters = param.getChoices(pendingArgs, interactionInitiatedBy);
+            final ManagedParameter paramMod,
+            final ParameterNegotiationModel paramNeg) {
+        val paramMeta = paramMod.getMetaModel();
+        val choiceAdapters = paramMeta.getChoices(paramNeg, getInteractionInitiatedBy());
         if (choiceAdapters == null || choiceAdapters.isEmpty()) {
             return null;
         }
@@ -176,13 +177,8 @@ extends AbstractObjectMemberReprRenderer<ObjectAction> {
         return list;
     }
 
-    private Object defaultFor(final ObjectActionParameter param) {
-
-        val emptyParamTuple = param.getAction()
-                .interactionHead(objectAdapter)
-                .emptyModel();
-
-        val defaultAdapter = param.getDefault(emptyParamTuple);
+    private Object defaultFor(final ManagedParameter paramMod) {
+        val defaultAdapter = paramMod.getValue().getValue();
         if (ManagedObjects.isNullOrUnspecifiedOrEmpty(defaultAdapter)) {
             return null;
         }