Skip to content

Commit 4b79f1b

Browse files
[Fix #928] Adding config and secret manager (#931)
* [Fix #928] Adding config and secret manager Signed-off-by: fjtirado <[email protected]> * Update impl/test/src/test/java/io/serverlessworkflow/impl/test/SecretExpressionTest.java Co-authored-by: Ricardo Zanini <[email protected]> --------- Signed-off-by: fjtirado <[email protected]> Co-authored-by: Ricardo Zanini <[email protected]>
1 parent accba1b commit 4b79f1b

File tree

11 files changed

+469
-9
lines changed

11 files changed

+469
-9
lines changed

impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
import io.serverlessworkflow.api.types.SchemaInline;
2121
import io.serverlessworkflow.api.types.Workflow;
2222
import io.serverlessworkflow.impl.additional.WorkflowAdditionalObject;
23+
import io.serverlessworkflow.impl.config.ConfigManager;
24+
import io.serverlessworkflow.impl.config.SecretManager;
25+
import io.serverlessworkflow.impl.config.SystemPropertyConfigManager;
2326
import io.serverlessworkflow.impl.events.EventConsumer;
2427
import io.serverlessworkflow.impl.events.EventPublisher;
2528
import io.serverlessworkflow.impl.events.InMemoryEvents;
@@ -37,6 +40,7 @@
3740
import java.util.ArrayList;
3841
import java.util.Collection;
3942
import java.util.Collections;
43+
import java.util.HashMap;
4044
import java.util.HashSet;
4145
import java.util.Map;
4246
import java.util.Optional;
@@ -64,6 +68,8 @@ public class WorkflowApplication implements AutoCloseable {
6468
private final WorkflowModelFactory modelFactory;
6569
private final WorkflowScheduler scheduler;
6670
private final Map<String, WorkflowAdditionalObject<?>> additionalObjects;
71+
private final ConfigManager configManager;
72+
private final SecretManager secretManager;
6773

6874
private WorkflowApplication(Builder builder) {
6975
this.taskFactory = builder.taskFactory;
@@ -82,6 +88,8 @@ private WorkflowApplication(Builder builder) {
8288
this.modelFactory = builder.modelFactory;
8389
this.scheduler = builder.scheduler;
8490
this.additionalObjects = builder.additionalObjects;
91+
this.configManager = builder.configManager;
92+
this.secretManager = builder.secretManager;
8593
}
8694

8795
public TaskExecutorFactory taskFactory() {
@@ -158,6 +166,8 @@ public SchemaValidator getValidator(SchemaInline inline) {
158166
private boolean lifeCycleCEPublishingEnabled = true;
159167
private WorkflowModelFactory modelFactory;
160168
private Map<String, WorkflowAdditionalObject<?>> additionalObjects;
169+
private SecretManager secretManager;
170+
private ConfigManager configManager;
161171

162172
private Builder() {}
163173

@@ -226,10 +236,20 @@ public Builder withEventPublisher(EventPublisher eventPublisher) {
226236
return this;
227237
}
228238

239+
public Builder withSecretManager(SecretManager secretManager) {
240+
this.secretManager = secretManager;
241+
return this;
242+
}
243+
244+
public Builder withConfigManager(ConfigManager configManager) {
245+
this.configManager = configManager;
246+
return this;
247+
}
248+
229249
public <T> Builder withAdditionalObject(
230250
String name, WorkflowAdditionalObject<T> additionalObject) {
231251
if (additionalObjects == null) {
232-
additionalObjects = new ConcurrentHashMap<>();
252+
additionalObjects = new HashMap<>();
233253
}
234254
additionalObjects.put(name, additionalObject);
235255
return this;
@@ -286,7 +306,18 @@ public WorkflowApplication build() {
286306
if (additionalObjects == null) {
287307
additionalObjects = Collections.emptyMap();
288308
}
289-
309+
if (configManager == null) {
310+
configManager =
311+
ServiceLoader.load(ConfigManager.class)
312+
.findFirst()
313+
.orElseGet(() -> new SystemPropertyConfigManager());
314+
}
315+
if (secretManager == null) {
316+
secretManager =
317+
ServiceLoader.load(SecretManager.class)
318+
.findFirst()
319+
.orElseGet(() -> s -> configManager.config(s, String.class));
320+
}
290321
return new WorkflowApplication(this);
291322
}
292323
}
@@ -348,6 +379,14 @@ public WorkflowScheduler scheduler() {
348379
return scheduler;
349380
}
350381

382+
public ConfigManager configManager() {
383+
return configManager;
384+
}
385+
386+
public SecretManager secretManager() {
387+
return secretManager;
388+
}
389+
351390
public <T> Optional<T> additionalObject(
352391
String name, WorkflowContext workflowContext, TaskContext taskContext) {
353392
return Optional.ofNullable(additionalObjects.get(name))

impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowError.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,36 @@
2020
public record WorkflowError(
2121
String type, int status, String instance, String title, String details) {
2222

23-
public static final Errors.Standard RUNTIME_TYPE = Errors.RUNTIME;
24-
public static final Errors.Standard COMM_TYPE = Errors.COMMUNICATION;
25-
2623
public static Builder error(String type, int status) {
2724
return new Builder(type, status);
2825
}
2926

27+
public static Builder authorization() {
28+
return error(Errors.AUTHORIZATION.toString(), Errors.AUTHORIZATION.status());
29+
}
30+
3031
public static Builder communication(int status, TaskContext context, Exception ex) {
3132
return communication(status, context, ex.getMessage());
3233
}
3334

3435
public static Builder communication(int status, TaskContext context, String title) {
35-
return new Builder(COMM_TYPE.toString(), status)
36+
return new Builder(Errors.COMMUNICATION.toString(), status)
3637
.instance(context.position().jsonPointer())
3738
.title(title);
3839
}
3940

4041
public static Builder communication(TaskContext context, String title) {
41-
return communication(COMM_TYPE.status(), context, title);
42+
return communication(Errors.COMMUNICATION.status(), context, title);
4243
}
4344

4445
public static Builder runtime(int status, TaskContext context, Exception ex) {
45-
return new Builder(RUNTIME_TYPE.toString(), status)
46+
return new Builder(Errors.RUNTIME.toString(), status)
4647
.instance(context.position().jsonPointer())
4748
.title(ex.getMessage());
4849
}
4950

5051
public static Builder runtime(TaskContext context, Exception ex) {
51-
return runtime(RUNTIME_TYPE.status(), context, ex);
52+
return runtime(Errors.RUNTIME.status(), context, ex);
5253
}
5354

5455
public static class Builder {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.config;
17+
18+
import java.time.Instant;
19+
import java.time.OffsetDateTime;
20+
import java.util.Optional;
21+
22+
public abstract class AbstractConfigManager implements ConfigManager {
23+
24+
@Override
25+
public <T> Optional<T> config(String propName, Class<T> propClass) {
26+
return Optional.ofNullable(get(propName)).map(v -> convert(v, propClass));
27+
}
28+
29+
protected abstract String get(String propName);
30+
31+
protected <T> T convert(String value, Class<T> propClass) {
32+
Object result;
33+
if (String.class.isAssignableFrom(propClass)) {
34+
result = value;
35+
} else if (Boolean.class.isAssignableFrom(propClass)) {
36+
result = Boolean.parseBoolean(value);
37+
} else if (Integer.class.isAssignableFrom(propClass)) {
38+
result = Integer.parseInt(value);
39+
} else if (Long.class.isAssignableFrom(propClass)) {
40+
result = Long.parseLong(value);
41+
} else if (Double.class.isAssignableFrom(propClass)) {
42+
result = Double.parseDouble(value);
43+
} else if (Float.class.isAssignableFrom(propClass)) {
44+
result = Float.parseFloat(value);
45+
} else if (Short.class.isAssignableFrom(propClass)) {
46+
result = Short.parseShort(value);
47+
} else if (Byte.class.isAssignableFrom(propClass)) {
48+
result = Byte.parseByte(value);
49+
} else if (Instant.class.isAssignableFrom(propClass)) {
50+
result = Instant.parse(value);
51+
} else if (OffsetDateTime.class.isAssignableFrom(propClass)) {
52+
result = OffsetDateTime.parse(value);
53+
} else {
54+
result = convertComplex(value, propClass);
55+
}
56+
return propClass.cast(result);
57+
}
58+
59+
protected abstract <T> T convertComplex(String value, Class<T> propClass);
60+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.config;
17+
18+
import io.serverlessworkflow.impl.ServicePriority;
19+
import java.util.Optional;
20+
21+
public interface ConfigManager extends ServicePriority {
22+
<T> Optional<T> config(String propName, Class<T> propClass);
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.config;
17+
18+
import io.serverlessworkflow.impl.ServicePriority;
19+
import java.util.Optional;
20+
21+
@FunctionalInterface
22+
public interface SecretManager extends ServicePriority {
23+
Optional<String> secret(String secretName);
24+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.config;
17+
18+
public class SystemPropertyConfigManager extends AbstractConfigManager {
19+
20+
@Override
21+
protected String get(String propName) {
22+
return System.getProperty(propName);
23+
}
24+
25+
@Override
26+
protected <T> T convertComplex(String value, Class<T> propClass) {
27+
throw new UnsupportedOperationException(
28+
"Conversion of property " + value + " to class " + propClass + " is not supported");
29+
}
30+
}

impl/jq/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
import com.fasterxml.jackson.databind.node.ArrayNode;
2222
import io.serverlessworkflow.impl.TaskContext;
2323
import io.serverlessworkflow.impl.WorkflowContext;
24+
import io.serverlessworkflow.impl.WorkflowError;
25+
import io.serverlessworkflow.impl.WorkflowException;
2426
import io.serverlessworkflow.impl.WorkflowModel;
2527
import io.serverlessworkflow.impl.expressions.ObjectExpression;
2628
import io.serverlessworkflow.impl.expressions.TaskDescriptor;
2729
import io.serverlessworkflow.impl.expressions.WorkflowDescriptor;
30+
import io.serverlessworkflow.impl.jackson.FunctionJsonNode;
2831
import io.serverlessworkflow.impl.jackson.JsonUtils;
2932
import java.util.function.Supplier;
3033
import net.thisptr.jackson.jq.Output;
@@ -91,6 +94,17 @@ private Scope createScope(WorkflowContext workflow, TaskContext task) {
9194
task.variables().forEach((k, v) -> childScope.setValue(k, JsonUtils.fromValue(v)));
9295
}
9396
if (workflow != null) {
97+
childScope.setValue(
98+
"secret",
99+
new FunctionJsonNode(
100+
k ->
101+
workflow
102+
.definition()
103+
.application()
104+
.secretManager()
105+
.secret(k)
106+
.orElseThrow(
107+
() -> new WorkflowException(WorkflowError.authorization().build()))));
94108
childScope.setValue("context", modelToJson(workflow.context()));
95109
childScope.setValue(
96110
"runtime",

0 commit comments

Comments
 (0)