From e0674284b2d4b047b7fe2b3c2f66dc8e04239bf9 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Fri, 31 Oct 2025 13:19:01 +0100 Subject: [PATCH 1/2] [Fix #928] Adding config and secret manager Signed-off-by: fjtirado --- .../impl/WorkflowApplication.java | 43 +++++- .../impl/WorkflowError.java | 15 +- .../impl/config/AbstractConfigManager.java | 60 ++++++++ .../impl/config/ConfigManager.java | 23 +++ .../impl/config/SecretManager.java | 24 ++++ .../config/SystemPropertyConfigManager.java | 30 ++++ .../impl/expressions/jq/JQExpression.java | 14 ++ .../impl/jackson/FunctionBaseJsonNode.java | 132 ++++++++++++++++++ .../impl/jackson/FunctionJsonNode.java | 34 +++++ .../impl/test/SecretExpressionTest.java | 88 ++++++++++++ .../workflows-samples/secret-expression.yaml | 9 ++ 11 files changed, 463 insertions(+), 9 deletions(-) create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/config/AbstractConfigManager.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/config/ConfigManager.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/config/SecretManager.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/config/SystemPropertyConfigManager.java create mode 100644 impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionBaseJsonNode.java create mode 100644 impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionJsonNode.java create mode 100644 impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java create mode 100644 impl/test/src/test/resources/workflows-samples/secret-expression.yaml diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index cd97368a8..5fe9b8c4d 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -20,6 +20,9 @@ import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.additional.WorkflowAdditionalObject; +import io.serverlessworkflow.impl.config.ConfigManager; +import io.serverlessworkflow.impl.config.SecretManager; +import io.serverlessworkflow.impl.config.SystemPropertyConfigManager; import io.serverlessworkflow.impl.events.EventConsumer; import io.serverlessworkflow.impl.events.EventPublisher; import io.serverlessworkflow.impl.events.InMemoryEvents; @@ -37,6 +40,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -64,6 +68,8 @@ public class WorkflowApplication implements AutoCloseable { private final WorkflowModelFactory modelFactory; private final WorkflowScheduler scheduler; private final Map> additionalObjects; + private final ConfigManager configManager; + private final SecretManager secretManager; private WorkflowApplication(Builder builder) { this.taskFactory = builder.taskFactory; @@ -82,6 +88,8 @@ private WorkflowApplication(Builder builder) { this.modelFactory = builder.modelFactory; this.scheduler = builder.scheduler; this.additionalObjects = builder.additionalObjects; + this.configManager = builder.configManager; + this.secretManager = builder.secretManager; } public TaskExecutorFactory taskFactory() { @@ -158,6 +166,8 @@ public SchemaValidator getValidator(SchemaInline inline) { private boolean lifeCycleCEPublishingEnabled = true; private WorkflowModelFactory modelFactory; private Map> additionalObjects; + private SecretManager secretManager; + private ConfigManager configManager; private Builder() {} @@ -226,10 +236,20 @@ public Builder withEventPublisher(EventPublisher eventPublisher) { return this; } + public Builder withSecretManager(SecretManager secretManager) { + this.secretManager = secretManager; + return this; + } + + public Builder withConfigManager(ConfigManager configManager) { + this.configManager = configManager; + return this; + } + public Builder withAdditionalObject( String name, WorkflowAdditionalObject additionalObject) { if (additionalObjects == null) { - additionalObjects = new ConcurrentHashMap<>(); + additionalObjects = new HashMap<>(); } additionalObjects.put(name, additionalObject); return this; @@ -286,7 +306,18 @@ public WorkflowApplication build() { if (additionalObjects == null) { additionalObjects = Collections.emptyMap(); } - + if (configManager == null) { + configManager = + ServiceLoader.load(ConfigManager.class) + .findFirst() + .orElseGet(() -> new SystemPropertyConfigManager()); + } + if (secretManager == null) { + secretManager = + ServiceLoader.load(SecretManager.class) + .findFirst() + .orElseGet(() -> s -> configManager.config(s, String.class)); + } return new WorkflowApplication(this); } } @@ -348,6 +379,14 @@ public WorkflowScheduler scheduler() { return scheduler; } + public ConfigManager configManager() { + return configManager; + } + + public SecretManager secretManager() { + return secretManager; + } + public Optional additionalObject( String name, WorkflowContext workflowContext, TaskContext taskContext) { return Optional.ofNullable(additionalObjects.get(name)) diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowError.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowError.java index c0952c014..d870c0cc2 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowError.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowError.java @@ -20,35 +20,36 @@ public record WorkflowError( String type, int status, String instance, String title, String details) { - public static final Errors.Standard RUNTIME_TYPE = Errors.RUNTIME; - public static final Errors.Standard COMM_TYPE = Errors.COMMUNICATION; - public static Builder error(String type, int status) { return new Builder(type, status); } + public static Builder authorization() { + return error(Errors.AUTHORIZATION.toString(), Errors.AUTHORIZATION.status()); + } + public static Builder communication(int status, TaskContext context, Exception ex) { return communication(status, context, ex.getMessage()); } public static Builder communication(int status, TaskContext context, String title) { - return new Builder(COMM_TYPE.toString(), status) + return new Builder(Errors.COMMUNICATION.toString(), status) .instance(context.position().jsonPointer()) .title(title); } public static Builder communication(TaskContext context, String title) { - return communication(COMM_TYPE.status(), context, title); + return communication(Errors.COMMUNICATION.status(), context, title); } public static Builder runtime(int status, TaskContext context, Exception ex) { - return new Builder(RUNTIME_TYPE.toString(), status) + return new Builder(Errors.RUNTIME.toString(), status) .instance(context.position().jsonPointer()) .title(ex.getMessage()); } public static Builder runtime(TaskContext context, Exception ex) { - return runtime(RUNTIME_TYPE.status(), context, ex); + return runtime(Errors.RUNTIME.status(), context, ex); } public static class Builder { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/config/AbstractConfigManager.java b/impl/core/src/main/java/io/serverlessworkflow/impl/config/AbstractConfigManager.java new file mode 100644 index 000000000..f12c341fe --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/config/AbstractConfigManager.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.config; + +import java.time.Instant; +import java.time.OffsetDateTime; +import java.util.Optional; + +public abstract class AbstractConfigManager implements ConfigManager { + + @Override + public Optional config(String propName, Class propClass) { + return Optional.ofNullable(get(propName)).map(v -> convert(v, propClass)); + } + + protected abstract String get(String propName); + + protected T convert(String value, Class propClass) { + Object result; + if (String.class.isAssignableFrom(propClass)) { + result = value; + } else if (Boolean.class.isAssignableFrom(propClass)) { + result = Boolean.parseBoolean(value); + } else if (Integer.class.isAssignableFrom(propClass)) { + result = Integer.parseInt(value); + } else if (Long.class.isAssignableFrom(propClass)) { + result = Long.parseLong(value); + } else if (Double.class.isAssignableFrom(propClass)) { + result = Double.parseDouble(value); + } else if (Float.class.isAssignableFrom(propClass)) { + result = Float.parseFloat(value); + } else if (Short.class.isAssignableFrom(propClass)) { + result = Short.parseShort(value); + } else if (Byte.class.isAssignableFrom(propClass)) { + result = Byte.parseByte(value); + } else if (Instant.class.isAssignableFrom(propClass)) { + result = Instant.parse(value); + } else if (OffsetDateTime.class.isAssignableFrom(propClass)) { + result = OffsetDateTime.parse(value); + } else { + result = convertComplex(value, propClass); + } + return propClass.cast(result); + } + + protected abstract T convertComplex(String value, Class propClass); +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/config/ConfigManager.java b/impl/core/src/main/java/io/serverlessworkflow/impl/config/ConfigManager.java new file mode 100644 index 000000000..9bdab4fe9 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/config/ConfigManager.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.config; + +import io.serverlessworkflow.impl.ServicePriority; +import java.util.Optional; + +public interface ConfigManager extends ServicePriority { + Optional config(String propName, Class propClass); +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/config/SecretManager.java b/impl/core/src/main/java/io/serverlessworkflow/impl/config/SecretManager.java new file mode 100644 index 000000000..04fc1e9cc --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/config/SecretManager.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.config; + +import io.serverlessworkflow.impl.ServicePriority; +import java.util.Optional; + +@FunctionalInterface +public interface SecretManager extends ServicePriority { + Optional secret(String secretName); +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/config/SystemPropertyConfigManager.java b/impl/core/src/main/java/io/serverlessworkflow/impl/config/SystemPropertyConfigManager.java new file mode 100644 index 000000000..5804611a3 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/config/SystemPropertyConfigManager.java @@ -0,0 +1,30 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.config; + +public class SystemPropertyConfigManager extends AbstractConfigManager { + + @Override + protected String get(String propName) { + return System.getProperty(propName); + } + + @Override + protected T convertComplex(String value, Class propClass) { + throw new UnsupportedOperationException( + "Conversion of property " + value + " to class " + propClass + " is not supported"); + } +} diff --git a/impl/jq/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java b/impl/jq/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java index 8234eb1b7..b32d6b39c 100644 --- a/impl/jq/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java +++ b/impl/jq/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java @@ -21,10 +21,13 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowError; +import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.expressions.ObjectExpression; import io.serverlessworkflow.impl.expressions.TaskDescriptor; import io.serverlessworkflow.impl.expressions.WorkflowDescriptor; +import io.serverlessworkflow.impl.jackson.FunctionJsonNode; import io.serverlessworkflow.impl.jackson.JsonUtils; import java.util.function.Supplier; import net.thisptr.jackson.jq.Output; @@ -91,6 +94,17 @@ private Scope createScope(WorkflowContext workflow, TaskContext task) { task.variables().forEach((k, v) -> childScope.setValue(k, JsonUtils.fromValue(v))); } if (workflow != null) { + childScope.setValue( + "secret", + new FunctionJsonNode( + k -> + workflow + .definition() + .application() + .secretManager() + .secret(k) + .orElseThrow( + () -> new WorkflowException(WorkflowError.authorization().build())))); childScope.setValue("context", modelToJson(workflow.context())); childScope.setValue( "runtime", diff --git a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionBaseJsonNode.java b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionBaseJsonNode.java new file mode 100644 index 000000000..0ce68c644 --- /dev/null +++ b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionBaseJsonNode.java @@ -0,0 +1,132 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.BaseJsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import com.fasterxml.jackson.databind.node.MissingNode; +import java.io.IOException; +import java.util.List; + +public class FunctionBaseJsonNode extends BaseJsonNode { + + private static final long serialVersionUID = 1L; + + @Override + public JsonToken asToken() { + return JsonToken.START_OBJECT; + } + + @Override + public boolean isObject() { + return true; + } + + @Override + public T deepCopy() { + return (T) this; + } + + @Override + public JsonNode get(int index) { + return MissingNode.getInstance(); + } + + @Override + public JsonNode path(String fieldName) { + return get(fieldName); + } + + @Override + public JsonNode path(int index) { + return MissingNode.getInstance(); + } + + @Override + protected JsonNode _at(JsonPointer ptr) { + return get(ptr.getMatchingProperty()); + } + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.OBJECT; + } + + @Override + public String asText() { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public JsonNode findValue(String fieldName) { + return get(fieldName); + } + + @Override + public JsonNode findParent(String fieldName) { + return null; + } + + @Override + public List findValues(String fieldName, List foundSoFar) { + foundSoFar.add(findValue(fieldName)); + return foundSoFar; + } + + @Override + public List findValuesAsText(String fieldName, List foundSoFar) { + foundSoFar.add(findValue(fieldName).asText()); + return foundSoFar; + } + + @Override + public List findParents(String fieldName, List foundSoFar) { + foundSoFar.add(findParent(fieldName)); + return foundSoFar; + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public void serialize(JsonGenerator jgen, SerializerProvider provider) throws IOException { + // not serialize + } + + @Override + public void serializeWithType( + JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { + // not serialize + } +} diff --git a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionJsonNode.java b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionJsonNode.java new file mode 100644 index 000000000..5152a5123 --- /dev/null +++ b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/FunctionJsonNode.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.jackson; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.function.Function; + +public class FunctionJsonNode extends FunctionBaseJsonNode { + + private static final long serialVersionUID = 1L; + private transient Function function; + + public FunctionJsonNode(Function function) { + this.function = function; + } + + @Override + public JsonNode get(String fieldName) { + return JsonUtils.fromValue(function.apply(fieldName)); + } +} diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java new file mode 100644 index 000000000..ea2c0c0a8 --- /dev/null +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.test; + +import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowException; +import java.io.IOException; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class SecretExpressionTest { + + private static Workflow workflow; + + @BeforeAll + static void init() throws IOException { + workflow = readWorkflowFromClasspath("workflows-samples/secret-expression.yaml"); + } + + @Test + void testDefault() { + System.setProperty("whoissuperman", "ClarkKent"); + try (WorkflowApplication appl = WorkflowApplication.builder().build()) { + assertThat( + appl.workflowDefinition(workflow) + .instance(Map.of()) + .start() + .join() + .asMap() + .orElseThrow() + .get("superSecret")) + .isEqualTo("ClarkKent"); + } finally { + System.clearProperty("whoissuperman"); + } + } + + @Test + void testMissing() { + try (WorkflowApplication appl = WorkflowApplication.builder().build()) { + assertThatThrownBy( + () -> + appl.workflowDefinition(workflow) + .instance(Map.of()) + .start() + .join() + .asMap() + .orElseThrow()) + .hasCauseInstanceOf(WorkflowException.class); + } + } + + @Test + void testCustom() { + try (WorkflowApplication appl = + WorkflowApplication.builder().withSecretManager(k -> Optional.of("ClarkKent")).build()) { + assertThat( + appl.workflowDefinition(workflow) + .instance(Map.of()) + .start() + .join() + .asMap() + .orElseThrow() + .get("superSecret")) + .isEqualTo("ClarkKent"); + } + } +} diff --git a/impl/test/src/test/resources/workflows-samples/secret-expression.yaml b/impl/test/src/test/resources/workflows-samples/secret-expression.yaml new file mode 100644 index 000000000..95364b594 --- /dev/null +++ b/impl/test/src/test/resources/workflows-samples/secret-expression.yaml @@ -0,0 +1,9 @@ +document: + dsl: '1.0.0-alpha5' + namespace: test + name: secret-expression + version: '0.1.0' +do: + - useExpression: + set: + superSecret: ${$secret.whoissuperman} \ No newline at end of file From a167c2796dede91d3950370c71e2555f499e14d4 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> Date: Fri, 31 Oct 2025 14:48:47 +0100 Subject: [PATCH 2/2] Update impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java Co-authored-by: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> --- .../serverlessworkflow/impl/test/SecretExpressionTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java index ea2c0c0a8..87c7a4b69 100644 --- a/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java @@ -27,6 +27,10 @@ import java.util.Optional; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.junit.jupiter.api.parallel.Resources; public class SecretExpressionTest { @@ -38,6 +42,8 @@ static void init() throws IOException { } @Test + @Execution(ExecutionMode.SAME_THREAD) + @ResourceLock(Resources.SYSTEM_PROPERTIES) void testDefault() { System.setProperty("whoissuperman", "ClarkKent"); try (WorkflowApplication appl = WorkflowApplication.builder().build()) {