From 6ec0ba5804d599768ef5dc10ac0cf845352b2f94 Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Fri, 10 Jan 2025 20:01:05 +0100 Subject: [PATCH] Improvements to variables and conditional rendering of activity settings --- .../dag/activities/ActivityWrapper.java | 2 +- .../dag/activities/impl/IdentityActivity.java | 3 ++ .../workflow/dag/annotations/BoolSetting.java | 11 ++++- .../dag/annotations/DoubleSetting.java | 11 ++++- .../dag/annotations/EntitySetting.java | 11 ++++- .../workflow/dag/annotations/IntSetting.java | 11 ++++- .../dag/annotations/StringSetting.java | 11 ++++- .../workflow/dag/settings/BoolSettingDef.java | 3 +- .../dag/settings/DoubleSettingDef.java | 3 +- .../dag/settings/EntitySettingDef.java | 3 +- .../workflow/dag/settings/IntSettingDef.java | 3 +- .../db/workflow/dag/settings/SettingDef.java | 48 ++++++++++++++++--- .../dag/settings/StringSettingDef.java | 3 +- .../dag/variables/ReadableVariableStore.java | 18 ++++--- .../workflow/dag/variables/VariableStore.java | 18 ++++--- .../db/workflow/models/ActivityModel.java | 3 ++ .../db/workflow/session/UserSession.java | 2 +- .../dag/activities/ActivityRegistryTest.java | 20 ++++++++ 18 files changed, 153 insertions(+), 31 deletions(-) diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/ActivityWrapper.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/ActivityWrapper.java index ef9e0abb5e..5d5de9e0f4 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/ActivityWrapper.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/ActivityWrapper.java @@ -132,7 +132,7 @@ public ActivityModel toModel( boolean includeState ) { List inTypeModels = inTypePreview.stream().map( inType -> inType.map( TypePreviewModel::of ).orElse( null ) ).toList(); String invalidReason = invalidStateReason == null ? null : invalidStateReason.toString(); - return new ActivityModel( type, id, serializableSettings, config, rendering, this.state, inTypeModels, invalidReason ); + return new ActivityModel( type, id, serializableSettings, config, rendering, this.state, inTypeModels, invalidReason, variables.getVariables() ); } else { return new ActivityModel( type, id, serializableSettings, config, rendering ); diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/impl/IdentityActivity.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/impl/IdentityActivity.java index 03e281dd87..cc400545e4 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/impl/IdentityActivity.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/activities/impl/IdentityActivity.java @@ -74,6 +74,9 @@ def my_function(): @IntSetting(key = "I1", displayName = "FIRST", defaultValue = 2, shortDescription = "This setting doesn't do anything.") @StringSetting(key = "S1", displayName = "SECOND", shortDescription = "This setting doesn't do anything.") +@IntSetting(key = "X1", displayName = "X1", shortDescription = "Depends on I1 being 42 or 420", subPointer = "I1", subValues = { "42", "420" }) +@StringSetting(key = "X2", displayName = "X2", shortDescription = "Depends on X1 being 3", subPointer = "I1", subValues = { "3" }) +@StringSetting(key = "X3", displayName = "X3", shortDescription = "Depends on I1/doesNotExist being 7", subPointer = "I1/doesNotExist", subValues = { "7" }) @Group(key = "groupA", displayName = "Group A", subgroups = { @Subgroup(key = "a", displayName = "Sub1") } diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/BoolSetting.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/BoolSetting.java index af95aa553a..5184e3ad97 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/BoolSetting.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/BoolSetting.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.polypheny.db.workflow.dag.settings.SettingDef; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -53,7 +54,15 @@ int position() default 100; - String subOf() default ""; + /** + * See {@link SettingDef#getSubPointer()} + */ + String subPointer() default ""; + + /** + * See {@link SettingDef#getSubValues()} + */ + String[] subValues() default {}; // String-specific settings boolean defaultValue() default false; diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/DoubleSetting.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/DoubleSetting.java index 90bc9a8bc4..471a0a0b36 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/DoubleSetting.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/DoubleSetting.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.polypheny.db.workflow.dag.settings.SettingDef; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -48,7 +49,15 @@ int position() default 100; - String subOf() default ""; + /** + * See {@link SettingDef#getSubPointer()} + */ + String subPointer() default ""; + + /** + * See {@link SettingDef#getSubValues()} + */ + String[] subValues() default {}; // Setting-specific properties diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/EntitySetting.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/EntitySetting.java index af57ca04e2..93460f56e5 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/EntitySetting.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/EntitySetting.java @@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.workflow.dag.settings.SettingDef; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -54,7 +55,15 @@ int position() default 100; - String subOf() default ""; + /** + * See {@link SettingDef#getSubPointer()} + */ + String subPointer() default ""; + + /** + * See {@link SettingDef#getSubValues()} + */ + String[] subValues() default {}; // Setting-specifics String defaultNamespace() default ""; diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/IntSetting.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/IntSetting.java index 23097caaac..08050ead01 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/IntSetting.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/IntSetting.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.polypheny.db.workflow.dag.settings.SettingDef; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -48,7 +49,15 @@ int position() default 100; - String subOf() default ""; + /** + * See {@link SettingDef#getSubPointer()} + */ + String subPointer() default ""; + + /** + * See {@link SettingDef#getSubValues()} + */ + String[] subValues() default {}; // Setting-specific properties diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/StringSetting.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/StringSetting.java index 9bbd4b5a53..0ebb5b0247 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/StringSetting.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/annotations/StringSetting.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.polypheny.db.workflow.dag.settings.SettingDef; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -53,7 +54,15 @@ int position() default 100; // manually impose order within subGroup (lower pos => further to the top) - String subOf() default ""; // determine the visibility of this setting, possibly using {@code SettingDef.SUB_SEP}. + /** + * See {@link SettingDef#getSubPointer()} + */ + String subPointer() default ""; + + /** + * See {@link SettingDef#getSubValues()} + */ + String[] subValues() default {}; // String-specific settings String defaultValue() default ""; diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/BoolSettingDef.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/BoolSettingDef.java index 938dd1fb75..e6b5b918a7 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/BoolSettingDef.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/BoolSettingDef.java @@ -28,7 +28,8 @@ public class BoolSettingDef extends SettingDef { public BoolSettingDef( BoolSetting a ) { - super( SettingType.BOOLEAN, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue() ), a.group(), a.subGroup(), a.position(), a.subOf() ); + super( SettingType.BOOLEAN, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue() ), + a.group(), a.subGroup(), a.position(), a.subPointer(), a.subValues() ); } diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/DoubleSettingDef.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/DoubleSettingDef.java index 4ace073f3e..88b59ec18e 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/DoubleSettingDef.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/DoubleSettingDef.java @@ -31,7 +31,8 @@ public class DoubleSettingDef extends SettingDef { public DoubleSettingDef( DoubleSetting a ) { - super( SettingType.DOUBLE, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue() ), a.group(), a.subGroup(), a.position(), a.subOf() ); + super( SettingType.DOUBLE, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue() ), + a.group(), a.subGroup(), a.position(), a.subPointer(), a.subValues() ); minValue = a.min(); maxValue = a.max(); diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/EntitySettingDef.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/EntitySettingDef.java index 880e993e11..016940eb68 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/EntitySettingDef.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/EntitySettingDef.java @@ -34,7 +34,8 @@ public class EntitySettingDef extends SettingDef { public EntitySettingDef( EntitySetting a ) { - super( SettingType.ENTITY, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultNamespace(), a.defaultName() ), a.group(), a.subGroup(), a.position(), a.subOf() ); + super( SettingType.ENTITY, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultNamespace(), a.defaultName() ), + a.group(), a.subGroup(), a.position(), a.subPointer(), a.subValues() ); this.dataModel = a.dataModel(); this.mustExist = a.mustExist(); } diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/IntSettingDef.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/IntSettingDef.java index aedfd4b34b..b153852978 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/IntSettingDef.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/IntSettingDef.java @@ -32,7 +32,8 @@ public class IntSettingDef extends SettingDef { public IntSettingDef( IntSetting a ) { - super( SettingType.INT, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue(), a.isList() ), a.group(), a.subGroup(), a.position(), a.subOf() ); + super( SettingType.INT, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue(), a.isList() ), + a.group(), a.subGroup(), a.position(), a.subPointer(), a.subValues() ); this.isList = a.isList(); this.minValue = a.min(); this.maxValue = a.max(); diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/SettingDef.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/SettingDef.java index 5ef792b825..ab3887d4f3 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/SettingDef.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/SettingDef.java @@ -17,6 +17,7 @@ package org.polypheny.db.workflow.dag.settings; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.lang.annotation.Annotation; @@ -31,6 +32,7 @@ import java.util.stream.Collectors; import lombok.Getter; import lombok.Value; +import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.util.Wrapper; import org.polypheny.db.workflow.dag.activities.ActivityException.InvalidSettingException; import org.polypheny.db.workflow.dag.annotations.ActivityDefinition; @@ -43,7 +45,7 @@ @Getter public abstract class SettingDef { - public static String SUB_SEP = ">"; // this separator is used to specify dependencies on values of other settings. For example "modeSelector>mode1" to specify that this setting is only active if modeSelector is equal to mode1 + private static final ObjectMapper MAPPER = new ObjectMapper(); private final SettingType type; private final String key; @@ -57,21 +59,55 @@ public abstract class SettingDef { private final String group; private final String subgroup; private final int position; - private final String subOf; + /** + * The subPointer is used to specify conditional rendering of this setting in the UI based on the value + * of another setting. An empty pointer disables conditional rendering for this setting. + *

+ * SubPointer is a JsonPointer (RFC6901) to a setting value. + * Note that "/" and "~" need to be escaped as ~1 and ~0 respectively. + * The leading "/" is optional in the constructor, as the first referenced object must always correspond to a setting name. + * Examples of valid pointers: + *

+ */ + private final String subPointer; - public SettingDef( SettingType type, String key, String displayName, String shortDescription, String longDescription, SettingValue defaultValue, String group, String subgroup, int position, String subOf ) { - assert !key.contains( SettingDef.SUB_SEP ) : "Setting key must not contain separator symbol '" + SUB_SEP + "': " + key; + /** + * SubValues is an array of values to compare the object at {@link #getSubPointer()} to. + * The setting is only rendered if at least one object is equal to a value of this array. + * The SettingDef constructor expects values specified as json. This implies that strings must be quoted! + */ + private final List subValues; + + + public SettingDef( + SettingType type, String key, String displayName, + String shortDescription, String longDescription, + SettingValue defaultValue, String group, String subgroup, + int position, String subPointer, String[] subValues ) { this.type = type; this.key = key; - this.displayName = displayName; + this.displayName = displayName.isBlank() ? key : displayName; this.shortDescription = shortDescription; this.longDescription = longDescription.isEmpty() ? shortDescription : longDescription; this.defaultValue = defaultValue; this.group = group; this.subgroup = subgroup; this.position = position; - this.subOf = subOf; + this.subPointer = (subPointer.isEmpty() || subPointer.startsWith( "/" )) ? subPointer : "/" + subPointer; + this.subValues = Arrays.stream( subValues ).map( v -> { + try { + return MAPPER.readTree( v ); + } catch ( JsonProcessingException e ) { + throw new GenericRuntimeException( "Invalid subValue for setting " + key + ": " + v, e ); + } + } ).toList(); } diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/StringSettingDef.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/StringSettingDef.java index b4fd06bead..d0c4ea713f 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/StringSettingDef.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/settings/StringSettingDef.java @@ -32,7 +32,8 @@ public class StringSettingDef extends SettingDef { public StringSettingDef( StringSetting a ) { - super( SettingType.STRING, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue(), a.isList() ), a.group(), a.subGroup(), a.position(), a.subOf() ); + super( SettingType.STRING, a.key(), a.displayName(), a.shortDescription(), a.longDescription(), getDefaultValue( a.defaultValue(), a.isList() ), + a.group(), a.subGroup(), a.position(), a.subPointer(), a.subValues() ); this.isList = a.isList(); this.minLength = a.minLength(); this.maxLength = a.maxLength(); diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/ReadableVariableStore.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/ReadableVariableStore.java index 2329c2ebf6..31f4f674ce 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/ReadableVariableStore.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/ReadableVariableStore.java @@ -25,8 +25,10 @@ public interface ReadableVariableStore { /** - * A variable reference is given as an object with a VARIABLE_REF_FIELD key and the reference as a string value. - * A reference corresponds to a JsonPointer (https://datatracker.ietf.org/doc/html/rfc6901). + * A variable reference is given as an object with a {@link #VARIABLE_REF_FIELD} key and the reference as a string value. + * Additionally, a default value can be specified with {@link #VARIABLE_DEFAULT_FIELD}. + *

+ * The reference corresponds to a JsonPointer (RFC6901). * Note that "/" and "~" in variable names need to be escaped as ~1 and ~0 respectively. * The leading "/" is optional, as the first referenced object must always correspond to a variable name. * Examples of valid references: @@ -39,6 +41,7 @@ public interface ReadableVariableStore { * */ String VARIABLE_REF_FIELD = "$ref"; + String VARIABLE_DEFAULT_FIELD = "$default"; boolean contains( String key ); @@ -58,24 +61,27 @@ public interface ReadableVariableStore { * Variable references are indicated by objects that contain a single field with name equal to the value of {@code VARIABLE_REF_FIELD}. * * @param node The node to be resolved recursively + * @param useDefaultIfMissing whether the default value specified by the variable reference is used in case the variable cannot be resolved. * @return a JsonNode with any variable references replaced by their value stored in this store. - * @throws IllegalArgumentException if a variable reference cannot be resolved in the variableMap + * @throws IllegalArgumentException if a variable reference cannot be resolved in the variableMap and no default value can be used (useDefaultIfMissing is false or no default exists). */ - JsonNode resolveVariables( JsonNode node ); + JsonNode resolveVariables( JsonNode node, boolean useDefaultIfMissing ); /** - * Resolves variables in the given map by recursively replacing any variable references with their resolved values. + * Resolves variables in the given map by recursively replacing any variable references with their resolved values + * or the default value specified by the reference, if the variable cannot be found. * * @param nodes The nodes to be resolved * @return an immutable map with JsonNodes that have any variable references replaced by their value stored in this store. - * @throws IllegalArgumentException if any {@link JsonNode} contains an unresolved variable reference. + * @throws IllegalArgumentException if any {@link JsonNode} contains an unresolved variable reference with no default value. */ Map resolveVariables( Map nodes ); /** * Resolves variables in the given map by recursively replacing any variable references with their resolved values. * If a variable cannot be resolved, inserts {@link Optional#empty()} for that variable. + * Default values are ignored. * * @param nodes The nodes to be resolved * @return an immutable map with resolved variables wrapped in {@link Optional}. diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/VariableStore.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/VariableStore.java index 12c1e97695..5952f656ac 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/VariableStore.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/dag/variables/VariableStore.java @@ -147,10 +147,10 @@ private void failIfReservedKey( String key ) { // TODO: make sure to not change the existing JsonNode - public JsonNode resolveVariables( JsonNode node ) { + public JsonNode resolveVariables( JsonNode node, boolean useDefaultIfMissing ) { if ( node.isObject() ) { ObjectNode objectNode = (ObjectNode) node; - if ( objectNode.size() == 1 && objectNode.has( VARIABLE_REF_FIELD ) ) { + if ( objectNode.size() <= 2 && objectNode.has( VARIABLE_REF_FIELD ) ) { String refString = objectNode.get( VARIABLE_REF_FIELD ).asText(); if ( refString.startsWith( "/" ) ) { refString = refString.substring( 1 ); @@ -164,7 +164,11 @@ public JsonNode resolveVariables( JsonNode node ) { // Replace the entire object with the value from the map, if it exists if ( replacement == null ) { - throw new IllegalArgumentException( "Cannot resolve variable with name: " + variableRef ); + if ( useDefaultIfMissing && objectNode.has( VARIABLE_DEFAULT_FIELD ) ) { + replacement = objectNode.get( VARIABLE_DEFAULT_FIELD ); + } else { + throw new IllegalArgumentException( "Cannot resolve variable with name: " + variableRef ); + } } return replacement; } else { @@ -172,14 +176,14 @@ public JsonNode resolveVariables( JsonNode node ) { Iterator> fields = objectNode.fields(); while ( fields.hasNext() ) { Map.Entry field = fields.next(); - objectNode.set( field.getKey(), resolveVariables( field.getValue() ) ); + objectNode.set( field.getKey(), resolveVariables( field.getValue(), useDefaultIfMissing ) ); } return objectNode; } } else if ( node.isArray() ) { // Recursively process child fields for ( int i = 0; i < node.size(); i++ ) { - ((ArrayNode) node).set( i, resolveVariables( node.get( i ) ) ); + ((ArrayNode) node).set( i, resolveVariables( node.get( i ), useDefaultIfMissing ) ); } return node; } else { @@ -192,7 +196,7 @@ public JsonNode resolveVariables( JsonNode node ) { public Map resolveVariables( Map nodes ) { Map resolved = new HashMap<>(); for ( Entry entry : nodes.entrySet() ) { - resolved.put( entry.getKey(), resolveVariables( entry.getValue() ) ); + resolved.put( entry.getKey(), resolveVariables( entry.getValue(), true ) ); } return Collections.unmodifiableMap( resolved ); } @@ -203,7 +207,7 @@ public Map> resolveAvailableVariables( Map> resolved = new HashMap<>(); for ( Map.Entry entry : nodes.entrySet() ) { try { - resolved.put( entry.getKey(), Optional.of( resolveVariables( entry.getValue() ) ) ); + resolved.put( entry.getKey(), Optional.of( resolveVariables( entry.getValue(), false ) ) ); } catch ( IllegalArgumentException e ) { resolved.put( entry.getKey(), Optional.empty() ); } diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/models/ActivityModel.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/models/ActivityModel.java index d89185c93f..f4779aaa84 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/models/ActivityModel.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/models/ActivityModel.java @@ -45,6 +45,8 @@ public class ActivityModel { List inTypePreview; @JsonInclude(JsonInclude.Include.NON_NULL) String invalidReason; // null if not invalid + @JsonInclude(JsonInclude.Include.NON_NULL) + Map variables; public ActivityModel( String type ) { @@ -71,6 +73,7 @@ public ActivityModel( String type, UUID id, Map settings, Acti this.state = null; this.inTypePreview = null; this.invalidReason = null; + this.variables = null; } diff --git a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/session/UserSession.java b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/session/UserSession.java index bb8c2cf891..b24896b9ed 100644 --- a/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/session/UserSession.java +++ b/plugins/workflow-engine/src/main/java/org/polypheny/db/workflow/session/UserSession.java @@ -94,8 +94,8 @@ public void handleRequest( UpdateActivityRequest request ) { broadcastMessage( new RenderingUpdateResponse( request.msgId, activity ) ); return; } - broadcastMessage( new StateUpdateResponse( request.msgId, workflow ) ); broadcastMessage( new ActivityUpdateResponse( request.msgId, activity ) ); + broadcastMessage( new StateUpdateResponse( request.msgId, workflow ) ); } diff --git a/plugins/workflow-engine/src/test/java/org/polypheny/db/workflow/dag/activities/ActivityRegistryTest.java b/plugins/workflow-engine/src/test/java/org/polypheny/db/workflow/dag/activities/ActivityRegistryTest.java index 37bed80309..739b869c9a 100644 --- a/plugins/workflow-engine/src/test/java/org/polypheny/db/workflow/dag/activities/ActivityRegistryTest.java +++ b/plugins/workflow-engine/src/test/java/org/polypheny/db/workflow/dag/activities/ActivityRegistryTest.java @@ -168,6 +168,26 @@ public void buildDefaultSettingValuesTest() throws InvalidSettingException { } + @Test + public void checkSubPointers() { + for ( ActivityDef activity : ActivityRegistry.getRegistry().values() ) { + String activityName = activity.getActivityClass().getSimpleName(); + + Set keys = activity.getSettings().keySet(); + + for ( SettingDef setting : activity.getSettings().values() ) { + String subPointer = setting.getSubPointer(); + if ( !subPointer.isEmpty() ) { + String targetKey = subPointer.substring( 1 ).split( "/", 2 )[0]; + assertNotEquals( setting.getKey(), targetKey ); + assertTrue( keys.contains( targetKey ), "Target setting key '" + targetKey + "' in subPointer is not defined for " + activityName ); + } + } + } + + } + + @Test public void intVariableResolveTest() throws InvalidSettingException { int newValue = 42;