Skip to content

Commit 321c79a

Browse files
authored
Merge pull request #1 from fjtirado/pr_comments
2 parents abb90cd + abf8e11 commit 321c79a

File tree

6 files changed

+214
-299
lines changed

6 files changed

+214
-299
lines changed

impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunScriptExecutor.java

Lines changed: 59 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -23,263 +23,105 @@
2323
import io.serverlessworkflow.impl.WorkflowApplication;
2424
import io.serverlessworkflow.impl.WorkflowContext;
2525
import io.serverlessworkflow.impl.WorkflowDefinition;
26-
import io.serverlessworkflow.impl.WorkflowError;
27-
import io.serverlessworkflow.impl.WorkflowException;
2826
import io.serverlessworkflow.impl.WorkflowModel;
2927
import io.serverlessworkflow.impl.WorkflowUtils;
3028
import io.serverlessworkflow.impl.WorkflowValueResolver;
3129
import io.serverlessworkflow.impl.resources.ResourceLoaderUtils;
32-
import java.util.Arrays;
33-
import java.util.HashMap;
30+
import io.serverlessworkflow.impl.scripts.ScriptContext;
31+
import io.serverlessworkflow.impl.scripts.ScriptLanguageId;
32+
import io.serverlessworkflow.impl.scripts.ScriptRunner;
3433
import java.util.Map;
34+
import java.util.Objects;
35+
import java.util.Optional;
3536
import java.util.ServiceLoader;
3637
import java.util.concurrent.CompletableFuture;
3738

3839
public class RunScriptExecutor implements RunnableTask<RunScript> {
3940

40-
public enum LanguageId {
41-
JS("js"),
42-
PYTHON("python");
41+
private Optional<WorkflowValueResolver<Map<String, Object>>> environmentExpr;
4342

44-
private final String lang;
43+
private Optional<WorkflowValueResolver<Map<String, Object>>> argumentExpr;
4544

46-
LanguageId(String lang) {
47-
this.lang = lang;
48-
}
49-
50-
public String getLang() {
51-
return lang;
52-
}
53-
54-
public static boolean isSupported(String lang) {
55-
for (LanguageId l : LanguageId.values()) {
56-
if (l.getLang().equalsIgnoreCase(lang)) {
57-
return true;
58-
}
59-
}
60-
return false;
61-
}
62-
}
63-
64-
@FunctionalInterface
65-
private interface CodeSupplier {
66-
String apply(WorkflowContext workflowContext, TaskContext taskContext);
67-
}
68-
69-
@SuppressWarnings("rawtypes")
70-
private Map<String, WorkflowValueResolver> environmentExpr;
71-
72-
@SuppressWarnings("rawtypes")
73-
private Map<String, WorkflowValueResolver> argumentExpr;
74-
75-
private CodeSupplier codeSupplier;
45+
private WorkflowValueResolver<String> codeSupplier;
7646
private boolean isAwait;
7747
private RunTaskConfiguration.ProcessReturnType returnType;
78-
private ScriptTaskRunner taskRunner;
48+
private ScriptRunner taskRunner;
7949

8050
@Override
8151
public void init(RunScript taskConfiguration, WorkflowDefinition definition) {
8252
ScriptUnion scriptUnion = taskConfiguration.getScript();
8353
Script script = scriptUnion.get();
84-
String language = scriptUnion.get().getLanguage();
85-
86-
WorkflowApplication application = definition.application();
87-
if (language == null || !LanguageId.isSupported(language)) {
88-
throw new IllegalArgumentException(
89-
"Unsupported script language: "
90-
+ language
91-
+ ". Supported languages are: "
92-
+ Arrays.toString(
93-
Arrays.stream(LanguageId.values()).map(LanguageId::getLang).toArray()));
94-
}
54+
ScriptLanguageId language = ScriptLanguageId.from(script.getLanguage());
9555

9656
this.taskRunner =
97-
ServiceLoader.load(ScriptTaskRunner.class)
57+
ServiceLoader.load(ScriptRunner.class).stream()
58+
.map(ServiceLoader.Provider::get)
59+
.filter(s -> s.identifier().equals(language))
9860
.findFirst()
9961
.orElseThrow(
100-
() -> new IllegalStateException("No implementation found for ScriptTaskRunner"));
62+
() ->
63+
new IllegalStateException(
64+
"No script runner implementation found for language " + language));
10165

10266
this.isAwait = taskConfiguration.isAwait();
10367

10468
this.returnType = taskConfiguration.getReturn();
10569

106-
if (script.getEnvironment() != null
107-
&& script.getEnvironment().getAdditionalProperties() != null) {
108-
this.environmentExpr =
109-
buildMapResolvers(application, script.getEnvironment().getAdditionalProperties());
110-
} else {
111-
this.environmentExpr = Map.of();
112-
}
113-
114-
if (script.getArguments() != null && script.getArguments().getAdditionalProperties() != null) {
115-
this.argumentExpr =
116-
buildMapResolvers(application, script.getArguments().getAdditionalProperties());
117-
} else {
118-
this.argumentExpr = Map.of();
119-
}
70+
WorkflowApplication application = definition.application();
71+
this.environmentExpr =
72+
script.getEnvironment() != null && script.getEnvironment().getAdditionalProperties() != null
73+
? Optional.of(
74+
WorkflowUtils.buildMapResolver(
75+
application, null, script.getEnvironment().getAdditionalProperties()))
76+
: Optional.empty();
77+
78+
this.argumentExpr =
79+
script.getArguments() != null && script.getArguments().getAdditionalProperties() != null
80+
? Optional.of(
81+
WorkflowUtils.buildMapResolver(
82+
application, null, script.getArguments().getAdditionalProperties()))
83+
: Optional.empty();
12084

12185
this.codeSupplier =
122-
(workflowContext, taskContext) -> {
123-
if (scriptUnion.getInlineScript() != null) {
124-
return scriptUnion.getInlineScript().getCode();
125-
} else if (scriptUnion.getExternalScript() == null) {
126-
throw new WorkflowException(
127-
WorkflowError.runtime(
128-
taskContext, new IllegalStateException("No script source defined."))
129-
.build());
130-
} else {
131-
return definition
132-
.resourceLoader()
133-
.load(
134-
scriptUnion.getExternalScript().getSource(),
135-
ResourceLoaderUtils::readString,
136-
workflowContext,
137-
taskContext,
138-
taskContext.input());
139-
}
140-
};
86+
scriptUnion.getInlineScript() != null
87+
? WorkflowUtils.buildStringFilter(application, scriptUnion.getInlineScript().getCode())
88+
: (w, t, m) ->
89+
definition
90+
.resourceLoader()
91+
.load(
92+
Objects.requireNonNull(
93+
scriptUnion.getExternalScript(),
94+
"External script is required if inline script was not set")
95+
.getSource(),
96+
ResourceLoaderUtils::readString,
97+
w,
98+
t,
99+
m);
141100
}
142101

143102
@Override
144103
public CompletableFuture<WorkflowModel> apply(
145104
WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) {
146-
147-
RunScriptContext.RunScriptContextBuilder builder =
148-
new RunScriptContext.RunScriptContextBuilder();
149-
150-
Map<String, String> envs = new HashMap<>();
151-
this.environmentExpr.forEach(
152-
(k, v) -> {
153-
Object resolved = v.apply(workflowContext, taskContext, input);
154-
envs.put(k, resolved.toString());
155-
});
156-
157-
Map<String, Object> args = new HashMap<>();
158-
this.argumentExpr.forEach(
159-
(k, v) -> {
160-
Object resolved = v.apply(workflowContext, taskContext, input);
161-
args.put(k, resolved);
162-
});
163-
164-
String code = this.codeSupplier.apply(workflowContext, taskContext);
165-
166-
RunScriptContext scriptContext =
167-
builder
168-
.withApplication(workflowContext.definition().application())
169-
.withReturnType(returnType)
170-
.withCode(code)
171-
.withArguments(args)
172-
.withEnvironment(envs)
173-
.withAwait(isAwait)
174-
.build();
175-
176105
return CompletableFuture.supplyAsync(
177-
() -> taskRunner.buildRun(taskContext).apply(scriptContext, input));
106+
() ->
107+
taskRunner.runScript(
108+
new ScriptContext(
109+
argumentExpr
110+
.map(m -> m.apply(workflowContext, taskContext, input))
111+
.orElse(Map.of()),
112+
environmentExpr
113+
.map(m -> m.apply(workflowContext, taskContext, input))
114+
.orElse(Map.of()),
115+
codeSupplier.apply(workflowContext, taskContext, input),
116+
isAwait,
117+
returnType),
118+
workflowContext,
119+
taskContext,
120+
input));
178121
}
179122

180123
@Override
181124
public boolean accept(Class<? extends RunTaskConfiguration> clazz) {
182125
return RunScript.class.equals(clazz);
183126
}
184-
185-
/** Builds a map of WorkflowValueResolvers from the provided properties. */
186-
@SuppressWarnings("rawtypes")
187-
private Map<String, WorkflowValueResolver> buildMapResolvers(
188-
WorkflowApplication application, Map<String, Object> properties) {
189-
Map<String, WorkflowValueResolver> resolvers = new HashMap<>();
190-
if (properties != null) {
191-
for (Map.Entry<String, Object> entry : properties.entrySet()) {
192-
WorkflowValueResolver<String> valueResolver =
193-
WorkflowUtils.buildStringFilter(application, entry.getValue().toString());
194-
resolvers.put(entry.getKey(), valueResolver);
195-
}
196-
}
197-
return resolvers;
198-
}
199-
200-
public static class RunScriptContext {
201-
private final WorkflowApplication application;
202-
private final Map<String, Object> args;
203-
private final Map<String, String> envs;
204-
private final String code;
205-
private final boolean isAwait;
206-
private final RunTaskConfiguration.ProcessReturnType returnType;
207-
208-
public RunScriptContext(RunScriptContextBuilder builder) {
209-
this.application = builder.application;
210-
this.args = builder.args;
211-
this.envs = builder.envs;
212-
this.code = builder.code;
213-
this.isAwait = builder.awaiting;
214-
this.returnType = builder.returnType;
215-
}
216-
217-
public Map<String, Object> getArgs() {
218-
return args;
219-
}
220-
221-
public Map<String, String> getEnvs() {
222-
return envs;
223-
}
224-
225-
public String getCode() {
226-
return code;
227-
}
228-
229-
public boolean isAwait() {
230-
return isAwait;
231-
}
232-
233-
public WorkflowApplication getApplication() {
234-
return application;
235-
}
236-
237-
public RunTaskConfiguration.ProcessReturnType getReturnType() {
238-
return returnType;
239-
}
240-
241-
public static class RunScriptContextBuilder {
242-
private Map<String, Object> args;
243-
private Map<String, String> envs;
244-
private String code;
245-
private boolean awaiting;
246-
private WorkflowApplication application;
247-
private RunTaskConfiguration.ProcessReturnType returnType;
248-
249-
public RunScriptContextBuilder withArguments(Map<String, Object> args) {
250-
this.args = args;
251-
return this;
252-
}
253-
254-
public RunScriptContextBuilder withEnvironment(Map<String, String> envs) {
255-
this.envs = envs;
256-
return this;
257-
}
258-
259-
public RunScriptContextBuilder withCode(String code) {
260-
this.code = code;
261-
return this;
262-
}
263-
264-
public RunScriptContextBuilder withAwait(boolean awaiting) {
265-
this.awaiting = awaiting;
266-
return this;
267-
}
268-
269-
public RunScriptContextBuilder withApplication(WorkflowApplication application) {
270-
this.application = application;
271-
return this;
272-
}
273-
274-
public RunScriptContextBuilder withReturnType(
275-
RunTaskConfiguration.ProcessReturnType returnType) {
276-
this.returnType = returnType;
277-
return this;
278-
}
279-
280-
public RunScriptContext build() {
281-
return new RunScriptContext(this);
282-
}
283-
}
284-
}
285127
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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.scripts;
17+
18+
import io.serverlessworkflow.api.types.RunTaskConfiguration;
19+
import java.util.Map;
20+
21+
public record ScriptContext(
22+
Map<String, Object> args,
23+
Map<String, Object> envs,
24+
String code,
25+
boolean isAwait,
26+
RunTaskConfiguration.ProcessReturnType returnType) {}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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.scripts;
17+
18+
import java.util.Arrays;
19+
20+
public enum ScriptLanguageId {
21+
JS("js"),
22+
PYTHON("python");
23+
24+
private final String lang;
25+
26+
ScriptLanguageId(String lang) {
27+
this.lang = lang;
28+
}
29+
30+
public String getLang() {
31+
return lang;
32+
}
33+
34+
public static ScriptLanguageId from(String lang) {
35+
for (ScriptLanguageId l : ScriptLanguageId.values()) {
36+
if (l.getLang().equalsIgnoreCase(lang)) {
37+
return l;
38+
}
39+
}
40+
throw new IllegalStateException(
41+
"Unsupported script language: "
42+
+ lang
43+
+ ". Supported languages are: "
44+
+ Arrays.toString(
45+
Arrays.stream(ScriptLanguageId.values()).map(ScriptLanguageId::getLang).toArray()));
46+
}
47+
}

0 commit comments

Comments
 (0)