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

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit ab2beb27e5893764f55fd114c234a325c26bfad5
Author: Alex Heneveld <[email protected]>
AuthorDate: Wed May 17 11:29:15 2023 +0100

    support adding time to an instant
---
 .../brooklyn/camp/brooklyn/WorkflowYamlTest.java   | 75 +++++++++++++++++++++-
 .../steps/variables/SetVariableWorkflowStep.java   |  6 ++
 .../variables/TransformVariableWorkflowStep.java   | 35 ++++++++--
 .../core/flags/BrooklynTypeNameResolution.java     |  2 +
 .../workflow/WorkflowInputOutputExtensionTest.java |  4 +-
 .../org/apache/brooklyn/util/time/Duration.java    |  8 +++
 6 files changed, 119 insertions(+), 11 deletions(-)

diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
index 7cfbc8329e..361bd08179 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
@@ -74,6 +74,9 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -1024,7 +1027,6 @@ public class WorkflowYamlTest extends AbstractYamlTest {
 
         Entity entity = Iterables.getOnlyElement(app.getChildren());
 
-        entity.sensors().set(Sensors.newStringSensor("good_interpolation"), 
"run");
         EntityAsserts.assertAttributeEventually(entity, 
Sensors.newSensor(Object.class, "mySum"), v -> v!=null);
         EntityAsserts.assertAttributeEquals(entity, 
Sensors.newSensor(Object.class, "mySum"), 7);
     }
@@ -1048,11 +1050,80 @@ public class WorkflowYamlTest extends AbstractYamlTest {
 
         Entity entity = Iterables.getOnlyElement(app.getChildren());
 
-        entity.sensors().set(Sensors.newStringSensor("good_interpolation"), 
"run");
         EntityAsserts.assertAttributeEventually(entity, 
Sensors.newSensor(Object.class, "mySum"), v -> v!=null);
         EntityAsserts.assertAttributeEquals(entity, 
Sensors.newSensor(Object.class, "mySum"), 7);
     }
 
+    @Test
+    public void testSumListOfDurations() throws Exception {
+        // same as above except list is of durations
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicEntity.class.getName(),
+                "  name: old-name",
+                "  brooklyn.initializers:",
+                "  - type: workflow-initializer",
+                "    brooklyn.config:",
+                "      name: post-init",
+                "      steps:",
+                "        - let duration d1 = 1d",
+                "        - step: transform y",
+                "          value:",
+                "            - ${d1}",
+                "            - 1h 1m",  // if first is a duration the others 
will be coerced
+                "          transform:",
+                "            - sum",
+                "            - to_string",
+                "        - set-sensor my_total_duration = ${y}",
+                "        - step: transform z",
+                "          value:",
+                "            - ${d1}",
+                "            - 1d 1s",
+                "            - ${d1}",
+                "          transform:",
+                "            - average",
+                "            - to_string",
+                "        - set-sensor my_average_duration = ${z}",
+                "");
+        waitForApplicationTasks(app);
+
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+
+        EntityAsserts.assertAttributeEventually(entity, 
Sensors.newSensor(Object.class, "my_total_duration"), v -> v!=null);
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newSensor(Object.class, "my_total_duration"), "1d 1h 1m");
+
+        EntityAsserts.assertAttributeEventually(entity, 
Sensors.newSensor(Object.class, "my_average_duration"), v -> v!=null);
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newSensor(Object.class, "my_average_duration"), "1d 333ms");
+    }
+
+    @Test
+    public void testAddDurationToInstant() throws Exception {
+        // same as above except list is of durations
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicEntity.class.getName(),
+                "  name: old-name",
+                "  brooklyn.initializers:",
+                "  - type: workflow-initializer",
+                "    brooklyn.config:",
+                "      name: post-init",
+                "      steps:",
+                "        - let instant x = ${workflow.util.now_iso}",
+                "        - let duration y = 7 days",
+                "        - let in_a_week = ${x} + ${y}",
+                "        - set-sensor in_a_week = ${in_a_week}",
+                "");
+        waitForApplicationTasks(app);
+
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+
+        EntityAsserts.assertAttributeEventually(entity, 
Sensors.newSensor(Object.class, "in_a_week"), v -> v!=null);
+        Object inAWeek = entity.sensors().get(Sensors.newSensor(Object.class, 
"in_a_week"));
+        Asserts.assertInstanceOf(inAWeek, Instant.class);
+        Asserts.assertThat((Instant)inAWeek, t -> 
t.isAfter(Instant.now().plus(6, ChronoUnit.DAYS)));
+        Asserts.assertThat((Instant)inAWeek, t -> 
t.isBefore(Instant.now().plus(8, ChronoUnit.DAYS)));
+    }
+
     @Test
     public void testSetCurrentEntityName() throws Exception {
         Entity app = createAndStartApplication(
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/SetVariableWorkflowStep.java
 
b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/SetVariableWorkflowStep.java
index 9e1c202521..e2ff30d695 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/SetVariableWorkflowStep.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/SetVariableWorkflowStep.java
@@ -33,11 +33,14 @@ import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.QuotedStringTokenizer;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Timestamp;
 import org.apache.commons.lang3.tuple.MutablePair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.annotation.Nullable;
+import java.time.Instant;
 import java.util.*;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -371,6 +374,9 @@ public class SetVariableWorkflowStep extends 
WorkflowStepDefinition {
             Object lhs = process(lhs0, null);
             Object rhs = process(rhs0, null);
 
+            if (lhs instanceof Instant) return TypeCoercions.coerce(rhs, 
Duration.class).addTo((Instant)lhs);
+            if (lhs instanceof Date) return new Timestamp((Instant) 
TypeCoercions.coerce(rhs, Duration.class).addTo( ((Date)lhs).toInstant() ));
+
             Maybe<Integer> lhsI = asInteger(lhs);
             Maybe<Integer> rhsI = asInteger(rhs);
             if (lhsI.isPresent() && rhsI.isPresent()) {
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/TransformVariableWorkflowStep.java
 
b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/TransformVariableWorkflowStep.java
index 5b795c3535..55efc1410a 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/TransformVariableWorkflowStep.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/variables/TransformVariableWorkflowStep.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.util.collections.*;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,7 +58,7 @@ public class TransformVariableWorkflowStep extends 
WorkflowStepDefinition {
 
     public static final ConfigKey<TypedValueToSet> VARIABLE = 
ConfigKeys.newConfigKey(TypedValueToSet.class, "variable");
     public static final ConfigKey<Object> VALUE = 
ConfigKeys.newConfigKey(Object.class, "value");
-    public static final ConfigKey<String> TRANSFORM = 
ConfigKeys.newConfigKey(String.class, "transform");
+    public static final ConfigKey<Object> TRANSFORM = 
ConfigKeys.newConfigKey(Object.class, "transform");
 
 
     @Override
@@ -83,8 +84,14 @@ public class TransformVariableWorkflowStep extends 
WorkflowStepDefinition {
         String name = 
context.resolve(WorkflowExpressionResolution.WorkflowExpressionStage.STEP_INPUT,
 variable.name, String.class);
         if (Strings.isBlank(name)) throw new 
IllegalArgumentException("Variable name is required");
 
-        String transform = context.getInput(TRANSFORM);
-        List<String> transforms = 
MutableList.copyOf(Arrays.stream(transform.split("\\|")).map(String::trim).collect(Collectors.toList()));
+        Object transformO = context.getInput(TRANSFORM);
+        if (!(transformO instanceof Iterable)) transformO = 
MutableList.of(transformO);
+        List<String> transforms = MutableList.of();
+        for (Object t: (Iterable)transformO) {
+            if (t instanceof String) 
transforms.addAll(MutableList.copyOf(Arrays.stream( ((String)t).split("\\|") 
).map(String::trim).collect(Collectors.toList())));
+            else throw new IllegalArgumentException("Argument to transform 
should be a string or list of strings, not: "+t);
+        }
+
 
         Object v;
         if (input.containsKey(VALUE.getName())) v = input.get(VALUE.getName());
@@ -176,6 +183,7 @@ public class TransformVariableWorkflowStep extends 
WorkflowStepDefinition {
             if (v instanceof Supplier) return ((Supplier)v).get();
             return v;
         });
+        TRANSFORMATIONS.put("to_string", () -> v -> Strings.toString(v));
     }
 
     static final Object minmax(Object v, String word, Predicate<Integer> test) 
{
@@ -190,12 +198,21 @@ public class TransformVariableWorkflowStep extends 
WorkflowStepDefinition {
         return result;
     }
 
-    static final Number sum(Object v, String word) {
+    static final Object sum(Object v, String word) {
         if (v==null) return null;
         if (!(v instanceof Iterable)) throw new 
IllegalArgumentException("Value is not an iterable; cannot take "+word);
+        Iterable<?> vi = (Iterable<?>) v;
+        if (vi.iterator().hasNext() && vi.iterator().next() instanceof 
Duration) {
+            Duration result = Duration.ZERO;
+            for (Object vii: vi) {
+                result = result.add(TypeCoercions.coerce(vii, Duration.class));
+            }
+            return result;
+        }
+
         double result = 0;
-        for (Object vi: (Iterable)v) {
-            result += asDouble(vi).get();
+        for (Object vii: vi) {
+            result += asDouble(vii).get();
         }
         return simplifiedToIntOrLongIfPossible(result);
     }
@@ -232,7 +249,11 @@ public class TransformVariableWorkflowStep extends 
WorkflowStepDefinition {
         if (v==null) return null;
         Integer count = size(v, "average");
         if (count==null || count==0) throw new IllegalArgumentException("Value 
is empty; cannot take "+word);
-        return simplifiedToIntOrLongIfPossible(asDouble(sum(v, 
"average")).get() / count);
+        Object sum = sum(v, "average");
+        if (sum instanceof Duration) {
+            return Duration.millis(Math.round(asDouble( 
((Duration)sum).toMilliseconds() ).get() / count) );
+        }
+        return simplifiedToIntOrLongIfPossible(asDouble(sum).get() / count);
     }
 
     @Override protected Boolean isDefaultIdempotent() { return true; }
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/flags/BrooklynTypeNameResolution.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/flags/BrooklynTypeNameResolution.java
index ee5568c444..b09db8e1be 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/flags/BrooklynTypeNameResolution.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/flags/BrooklynTypeNameResolution.java
@@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory;
 import javax.annotation.Nullable;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.time.Instant;
 import java.util.*;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -78,6 +79,7 @@ public class BrooklynTypeNameResolution {
 
             .put("duration", Duration.class)
             .put("timestamp", Timestamp.class)
+            .put("instant", Instant.class)
             .put("port", PortRange.class)
             .build();
 
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
index 2a7b48bd3e..81e3c5c0c5 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
@@ -233,9 +233,9 @@ public class WorkflowInputOutputExtensionTest extends 
BrooklynMgmtUnitTestSuppor
         };
         Asserts.assertEquals(transform.apply("min"), 1);
         Asserts.assertEquals(transform.apply("max"), 2);
-        Asserts.assertEquals(transform.apply("sum"), 3d);
+        Asserts.assertEquals(transform.apply("sum"), 3);
         Asserts.assertEquals(transform.apply("size"), 2);
-        Asserts.assertEquals(transform.apply("average"), 1.5d);
+        Asserts.assertEquals(transform.apply("average"), 1.5);
         Asserts.assertEquals(transform.apply("first"), 1);
         Asserts.assertEquals(transform.apply("last"), 2);
     }
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java
index 92bcc682fc..20a2e78d02 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java
@@ -22,12 +22,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.time.temporal.ChronoUnit.NANOS;
+import static java.time.temporal.ChronoUnit.SECONDS;
+
 import com.google.common.base.Stopwatch;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.Instant;
+import java.time.temporal.Temporal;
 import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
 import org.apache.brooklyn.util.text.Strings;
@@ -346,4 +350,8 @@ public class Duration implements Comparable<Duration>, 
Serializable {
         return this;
     }
 
+    public Object addTo(Temporal temporal) {
+        return temporal.plus(nanos, NANOS);
+    }
+
 }

Reply via email to