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 2022/04/06 17:58:06 UTC

[isis] branch master updated: ISIS-2297: have interaction API use its own InteractionRailway

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


The following commit(s) were added to refs/heads/master by this push:
     new a6a49812a5 ISIS-2297: have interaction API use its own InteractionRailway<T>
a6a49812a5 is described below

commit a6a49812a5812b29dc2d28a4669d4ea847bb4954
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Apr 6 19:57:56 2022 +0200

    ISIS-2297: have interaction API use its own InteractionRailway<T>
---
 .../applib/value/semantics/ValueDecomposition.java |  5 +-
 .../org/apache/isis/commons/functional/Either.java |  1 +
 .../apache/isis/commons/functional/Railway.java    |  1 +
 .../interactions/managed/ActionInteraction.java    | 12 ++--
 .../managed/CollectionInteraction.java             |  9 ++-
 .../interactions/managed/InteractionRailway.java   | 67 ++++++++++++++++++++++
 .../interactions/managed/MemberInteraction.java    |  9 ++-
 .../interactions/managed/PropertyInteraction.java  | 11 ++--
 .../viewer/resources/ObjectActionArgHelper.java    | 46 ++++++++-------
 9 files changed, 116 insertions(+), 45 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueDecomposition.java b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueDecomposition.java
index 44194e5c31..c7d5faa847 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueDecomposition.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueDecomposition.java
@@ -41,9 +41,6 @@ implements
     Serializable {
     private static final long serialVersionUID = 1L;
 
-    @Getter
-    private final Either<ValueWithTypeDto, TypedTupleDto> either;
-
     public static ValueDecomposition ofFundamental(final ValueWithTypeDto valueWithTypeDto) {
         return new ValueDecomposition(Either.left(valueWithTypeDto));
     }
@@ -52,6 +49,8 @@ implements
         return new ValueDecomposition(Either.right(typedTupleDto));
     }
 
+    @Getter private final Either<ValueWithTypeDto, TypedTupleDto> either;
+
     // used by RO-Viewer to render values
     public String toJson() {
         return this.<String>fold(
diff --git a/commons/src/main/java/org/apache/isis/commons/functional/Either.java b/commons/src/main/java/org/apache/isis/commons/functional/Either.java
index 60670693ca..5e32430a3d 100644
--- a/commons/src/main/java/org/apache/isis/commons/functional/Either.java
+++ b/commons/src/main/java/org/apache/isis/commons/functional/Either.java
@@ -189,6 +189,7 @@ public interface Either<L, R>  {
 
     // -- TYPE COMPOSITION
 
+    @FunctionalInterface
     public static interface HasEither<L, R> extends Either<L, R> {
 
         Either<L, R> getEither();
diff --git a/commons/src/main/java/org/apache/isis/commons/functional/Railway.java b/commons/src/main/java/org/apache/isis/commons/functional/Railway.java
index 5761efe2d1..256dd93088 100644
--- a/commons/src/main/java/org/apache/isis/commons/functional/Railway.java
+++ b/commons/src/main/java/org/apache/isis/commons/functional/Railway.java
@@ -231,6 +231,7 @@ public interface Railway<F, S> {
 
     // -- TYPE COMPOSITION
 
+    @FunctionalInterface
     public static interface HasRailway<F, S> extends Railway<F, S> {
 
         Railway<F, S> getRailway();
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 32ae1081b2..750c55d0d8 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
@@ -70,9 +70,9 @@ extends MemberInteraction<ManagedAction, ActionInteraction> {
 
         val managedAction = ManagedAction.lookupActionWithMultiselect(owner, memberId, where, multiselectChoices);
 
-        final Railway<InteractionVeto, ManagedAction> railway = managedAction.isPresent()
-                ? Railway.success(managedAction.get())
-                : Railway.failure(InteractionVeto.notFound(MemberType.ACTION, memberId));
+        final InteractionRailway<ManagedAction> railway = managedAction.isPresent()
+                ? InteractionRailway.success(managedAction.get())
+                : InteractionRailway.veto(InteractionVeto.notFound(MemberType.ACTION, memberId));
 
         return new ActionInteraction(
                 managedAction.map(ManagedAction::getAction),
@@ -82,18 +82,18 @@ extends MemberInteraction<ManagedAction, ActionInteraction> {
     public static ActionInteraction wrap(final @NonNull ManagedAction managedAction) {
         return new ActionInteraction(
                 Optional.of(managedAction.getAction()),
-                Railway.success(managedAction));
+                InteractionRailway.success(managedAction));
     }
 
     public static ActionInteraction empty(final String actionId) {
         return new ActionInteraction(
                 Optional.empty(),
-                Railway.failure(InteractionVeto.notFound(MemberType.ACTION, actionId)));
+                InteractionRailway.veto(InteractionVeto.notFound(MemberType.ACTION, actionId)));
     }
 
     ActionInteraction(
             final @NonNull Optional<ObjectAction> metamodel,
-            final @NonNull Railway<InteractionVeto, ManagedAction> railway) {
+            final @NonNull InteractionRailway<ManagedAction> railway) {
         super(railway);
         this.metamodel = metamodel;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/CollectionInteraction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/CollectionInteraction.java
index 4834806797..09aff8d9bf 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/CollectionInteraction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/CollectionInteraction.java
@@ -22,7 +22,6 @@ import java.util.Optional;
 import java.util.function.Function;
 
 import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.commons.functional.Railway;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedMember.MemberType;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 
@@ -39,14 +38,14 @@ extends MemberInteraction<ManagedCollection, CollectionInteraction> {
 
         val managedCollection = ManagedCollection.lookupCollection(owner, memberId, where);
 
-        final Railway<InteractionVeto, ManagedCollection> railway = managedCollection.isPresent()
-                ? Railway.success(managedCollection.get())
-                : Railway.failure(InteractionVeto.notFound(MemberType.COLLECTION, memberId));
+        final InteractionRailway<ManagedCollection> railway = managedCollection.isPresent()
+                ? InteractionRailway.success(managedCollection.get())
+                : InteractionRailway.veto(InteractionVeto.notFound(MemberType.COLLECTION, memberId));
 
         return new CollectionInteraction(railway);
     }
 
-    CollectionInteraction(@NonNull final Railway<InteractionVeto, ManagedCollection> railway) {
+    CollectionInteraction(@NonNull final InteractionRailway<ManagedCollection> railway) {
         super(railway);
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/InteractionRailway.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/InteractionRailway.java
new file mode 100644
index 0000000000..79df4b1a0e
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/InteractionRailway.java
@@ -0,0 +1,67 @@
+/*
+ *  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 java.io.Serializable;
+import java.util.function.Function;
+
+import org.apache.isis.commons.functional.Railway;
+import org.apache.isis.commons.functional.Railway.HasRailway;
+
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+import lombok.val;
+
+/**
+ * Follows the <em>Railway Pattern</em>, that is, once vetoed, stays vetoed.
+ * @see Railway
+ */
+@RequiredArgsConstructor(access=AccessLevel.PROTECTED)
+@ToString @EqualsAndHashCode
+public final class InteractionRailway<T extends ManagedMember>
+implements
+    HasRailway<InteractionVeto, T>,
+    Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static <T extends ManagedMember> InteractionRailway<T> success(final T managedMember) {
+        return new InteractionRailway<T>(Railway.<InteractionVeto, T>success(managedMember));
+    }
+
+    public static <T extends ManagedMember> InteractionRailway<T> veto(final InteractionVeto veto) {
+        return new InteractionRailway<>(Railway.<InteractionVeto, T>failure(veto));
+    }
+
+    @Getter private final Railway<InteractionVeto, T> railway;
+
+    @Override // type-safe override
+    public InteractionRailway<T> chain(final @NonNull Function<T, Railway<InteractionVeto, T>> chainingFunction) {
+        val railway = HasRailway.super.chain(chainingFunction);
+        return railway instanceof InteractionRailway
+            ? (InteractionRailway<T>) railway
+            : new InteractionRailway<>(railway);
+    }
+
+}
+
+
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MemberInteraction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MemberInteraction.java
index 512d9675f1..d720f548b9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MemberInteraction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/MemberInteraction.java
@@ -21,7 +21,6 @@ package org.apache.isis.core.metamodel.interactions.managed;
 import java.util.Optional;
 import java.util.function.Function;
 
-import org.apache.isis.commons.functional.Railway;
 import org.apache.isis.commons.internal.base._Casts;
 
 import lombok.NonNull;
@@ -37,9 +36,9 @@ public abstract class MemberInteraction<T extends ManagedMember, H extends Membe
         }
     }
 
-    @NonNull protected Railway<InteractionVeto, T> railway;
+    @NonNull protected InteractionRailway<T> railway;
 
-    protected MemberInteraction(@NonNull final Railway<InteractionVeto, T> railway) {
+    protected MemberInteraction(@NonNull final InteractionRailway<T> railway) {
         this.railway = railway;
     }
 
@@ -106,8 +105,8 @@ public abstract class MemberInteraction<T extends ManagedMember, H extends Membe
         return railway.getSuccessElseFail(onFailure);
     }
 
-    protected Railway<InteractionVeto, T> vetoRailway(final InteractionVeto veto) {
-        return Railway.failure(veto);
+    protected InteractionRailway<T> vetoRailway(final InteractionVeto veto) {
+        return InteractionRailway.veto(veto);
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/PropertyInteraction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/PropertyInteraction.java
index 177e5970ac..f9a74676b0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/PropertyInteraction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/PropertyInteraction.java
@@ -22,7 +22,6 @@ import java.util.Optional;
 import java.util.function.Function;
 
 import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.commons.functional.Railway;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedMember.MemberType;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 
@@ -39,18 +38,18 @@ extends MemberInteraction<ManagedProperty, PropertyInteraction> {
 
         val managedProperty = ManagedProperty.lookupProperty(owner, memberId, where);
 
-        final Railway<InteractionVeto, ManagedProperty> railway = managedProperty.isPresent()
-                ? Railway.success(managedProperty.get())
-                : Railway.failure(InteractionVeto.notFound(MemberType.PROPERTY, memberId));
+        final InteractionRailway<ManagedProperty> railway = managedProperty.isPresent()
+                ? InteractionRailway.success(managedProperty.get())
+                : InteractionRailway.veto(InteractionVeto.notFound(MemberType.PROPERTY, memberId));
 
         return new PropertyInteraction(railway);
     }
 
     public static PropertyInteraction wrap(final @NonNull ManagedProperty managedProperty) {
-        return new PropertyInteraction(Railway.success(managedProperty));
+        return new PropertyInteraction(InteractionRailway.success(managedProperty));
     }
 
-    PropertyInteraction(@NonNull final Railway<InteractionVeto, ManagedProperty> railway) {
+    PropertyInteraction(@NonNull final InteractionRailway<ManagedProperty> railway) {
         super(railway);
     }
 
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 31481ceda0..ee7444504c 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
@@ -20,6 +20,7 @@ package org.apache.isis.viewer.restfulobjects.viewer.resources;
 
 import java.util.List;
 import java.util.Map;
+import java.util.stream.IntStream;
 
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.functional.Railway;
@@ -50,35 +51,36 @@ public class ObjectActionArgHelper {
             final JsonRepresentation arguments) {
 
         val jsonArgList = argListFor(action, arguments);
-
-        final List<Railway<InteractionVeto, ManagedObject>> argAdapters = _Lists.newArrayList();
         val parameters = action.getParameters();
-        for (int i = 0; i < jsonArgList.size(); i++) {
-            final JsonRepresentation argRepr = jsonArgList.get(i);
-            final int argIndex = i;
+
+        return IntStream.range(0, jsonArgList.size())
+        .mapToObj(argIndex->{
+            final JsonRepresentation argRepr = jsonArgList.get(argIndex);
             val paramMeta = parameters.getElseFail(argIndex);
             val paramSpec = paramMeta.getElementType();
 
-            val objectOrVeto = Try.call(()->
-                    (paramMeta.isOptional() && argRepr == null)
+            val tryArgument = Try.call(()->
+                    (paramMeta.isOptional()
+                            && argRepr == null)
                     ? ManagedObject.empty(paramSpec)
                     : new JsonParserHelper(resourceContext, paramSpec)
-                            .objectAdapterFor(argRepr))
-            .<Railway<InteractionVeto, ManagedObject>>fold(
+                            .objectAdapterFor(argRepr));
+
+            val objectOrVeto = tryArgument.<Railway<InteractionVeto, ManagedObject>>fold(
                     exception->Railway.failure(
                             InteractionVeto.actionParamInvalid(
                                     String.format("exception when parsing paramNr %d [%s]: %s",
                                             argIndex, argRepr, exception))),
-                    success->Railway.success(success.orElseThrow()))
-            ;
+                    success->Railway.success(success.orElseThrow()));
 
-            argAdapters.add(objectOrVeto);
-        }
-        return Can.ofCollection(argAdapters);
+            return objectOrVeto;
+        })
+        .collect(Can.toCan());
     }
 
-    private static List<JsonRepresentation> argListFor(final ObjectAction action, final JsonRepresentation arguments) {
-        final List<JsonRepresentation> argList = _Lists.newArrayList();
+    private static List<JsonRepresentation> argListFor(
+            final ObjectAction action,
+            final JsonRepresentation arguments) {
 
         // ensure that we have no arguments that are not parameters
         arguments.streamMapEntries()
@@ -88,20 +90,24 @@ public class ObjectActionArgHelper {
             if (action.getParameterById(argName) == null) {
                 String reason = String.format("Argument '%s' found but no such parameter", argName);
                 arguments.mapPut("x-ro-invalidReason", reason);
-                throw RestfulObjectsApplicationException.createWithBody(RestfulResponse.HttpStatusCode.BAD_REQUEST, arguments, reason);
+                throw RestfulObjectsApplicationException
+                    .createWithBody(RestfulResponse.HttpStatusCode.BAD_REQUEST, arguments, reason);
             }
         });
 
         // ensure that an argument value has been provided for all non-optional
         // parameters
+        val argList = _Lists.<JsonRepresentation>newArrayList();
         val parameters = action.getParameters();
         for (final ObjectActionParameter param : parameters) {
             final String paramId = param.getId();
             final JsonRepresentation argRepr = arguments.getRepresentation(paramId);
-            if (argRepr == null && !param.isOptional()) {
-                String reason = String.format("No argument found for (mandatory) parameter '%s'", paramId);
+            if (argRepr == null
+                    && !param.isOptional()) {
+                val reason = String.format("No argument found for (mandatory) parameter '%s'", paramId);
                 arguments.mapPut("x-ro-invalidReason", reason);
-                throw RestfulObjectsApplicationException.createWithBody(RestfulResponse.HttpStatusCode.BAD_REQUEST, arguments, reason);
+                throw RestfulObjectsApplicationException
+                    .createWithBody(RestfulResponse.HttpStatusCode.BAD_REQUEST, arguments, reason);
             }
             argList.add(argRepr);
         }