Skip to content

Commit 6c9eca2

Browse files
committed
fix: update wait and set task syntax
fix: update wait and set task syntax
1 parent e2cf517 commit 6c9eca2

File tree

4 files changed

+217
-33
lines changed

4 files changed

+217
-33
lines changed

builders/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,12 @@ mod unit_tests {
434434
.iter()
435435
.any(|entry| entry.get(&set_task_name.to_string()).map_or(false, |task|{
436436
if let TaskDefinition::Set(set_task) = task {
437-
set_task.set == set_task_variables.clone()
437+
match &set_task.set {
438+
serverless_workflow_core::models::task::SetValue::Map(map) => {
439+
map == &set_task_variables
440+
}
441+
_ => false
442+
}
438443
}
439444
else{
440445
false
@@ -482,12 +487,12 @@ mod unit_tests {
482487
.iter()
483488
.any(|entry| entry.get(&wait_task_name.to_string()).map_or(false, |task| {
484489
if let TaskDefinition::Wait(wait_task) = task {
485-
wait_task.duration == wait_duration
490+
wait_task.wait == wait_duration
486491
} else {
487492
false
488493
}
489494
})),
490-
"Expected a task with key '{}' and a WaitTaskDefinition with 'duration'={}",
495+
"Expected a task with key '{}' and a WaitTaskDefinition with 'wait'={}",
491496
wait_task_name,
492497
wait_duration);
493498
}

builders/src/services/task.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,13 +1042,29 @@ impl SetTaskDefinitionBuilder{
10421042

10431043
/// Sets the specified variable
10441044
pub fn variable(&mut self, name: &str, value: Value) -> &mut Self{
1045-
self.task.set.insert(name.to_string(), value);
1045+
match &mut self.task.set {
1046+
serverless_workflow_core::models::task::SetValue::Map(map) => {
1047+
map.insert(name.to_string(), value);
1048+
}
1049+
serverless_workflow_core::models::task::SetValue::Expression(_) => {
1050+
// If it was an expression, convert to map
1051+
let mut map = HashMap::new();
1052+
map.insert(name.to_string(), value);
1053+
self.task.set = serverless_workflow_core::models::task::SetValue::Map(map);
1054+
}
1055+
}
1056+
self
1057+
}
1058+
1059+
/// Configures the variable as an expression
1060+
pub fn variable_expression(&mut self, expression: String) -> &mut Self{
1061+
self.task.set = serverless_workflow_core::models::task::SetValue::Expression(expression);
10461062
self
10471063
}
10481064

10491065
/// Configures the task to set the specified variables
10501066
pub fn variables(&mut self, variables: HashMap<String, Value>) -> &mut Self{
1051-
self.task.set = variables;
1067+
self.task.set = serverless_workflow_core::models::task::SetValue::Map(variables);
10521068
self
10531069
}
10541070

@@ -1820,18 +1836,18 @@ impl ScriptProcessDefinitionBuilder{
18201836
}
18211837

18221838
/// Adds a new argument to execute the script with
1823-
pub fn with_argument(&mut self, key: &str, value: &str) -> &mut Self{
1839+
pub fn with_argument(&mut self, value: &str) -> &mut Self{
18241840
if self.process.arguments.is_none(){
1825-
self.process.arguments = Some(HashMap::new());
1841+
self.process.arguments = Some(Vec::new());
18261842
}
18271843
if let Some(arguments) = &mut self.process.arguments {
1828-
arguments.insert(key.to_string(), value.to_string());
1844+
arguments.push(value.to_string());
18291845
}
18301846
self
18311847
}
18321848

18331849
/// Sets the arguments of the script to execute
1834-
pub fn with_arguments(&mut self, arguments: HashMap<String, String>) -> &mut Self{
1850+
pub fn with_arguments(&mut self, arguments: Vec<String>) -> &mut Self{
18351851
self.process.arguments = Some(arguments);
18361852
self
18371853
}
@@ -1853,6 +1869,11 @@ impl ScriptProcessDefinitionBuilder{
18531869
self
18541870
}
18551871

1872+
pub fn with_stdin(&mut self, stdin: &str) -> &mut Self{
1873+
self.process.stdin = Some(stdin.to_string());
1874+
self
1875+
}
1876+
18561877
/// Builds the configured RunTaskDefinition
18571878
pub fn build(self) -> RunTaskDefinition{
18581879
let mut run_task = RunTaskDefinition::default();

core/src/lib.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,140 @@ mod unit_tests {
237237
}
238238
}
239239
}
240+
241+
#[test]
242+
fn test_set_value_map_deserialization() {
243+
// Test SetValue with a map (object) of key-value pairs
244+
let set_value_map = serde_json::json!({
245+
"foo": "bar",
246+
"count": 42
247+
});
248+
249+
let result: Result<SetTaskDefinition, _> = serde_json::from_value(serde_json::json!({
250+
"set": set_value_map
251+
}));
252+
assert!(result.is_ok(), "Failed to deserialize set task with map: {:?}", result.err());
253+
254+
let set_task = result.unwrap();
255+
match set_task.set {
256+
SetValue::Map(map) => {
257+
assert_eq!(map.len(), 2);
258+
assert_eq!(map.get("foo").and_then(|v| v.as_str()), Some("bar"));
259+
assert_eq!(map.get("count").and_then(|v| v.as_u64()), Some(42));
260+
}
261+
SetValue::Expression(_) => {
262+
panic!("Expected SetValue::Map but got SetValue::Expression");
263+
}
264+
}
265+
}
266+
267+
#[test]
268+
fn test_set_value_expression_deserialization() {
269+
// Test SetValue with a runtime expression string
270+
let set_value_expr_json = serde_json::json!("${ $workflow.input[0] }");
271+
272+
let result: Result<SetTaskDefinition, _> = serde_json::from_value(serde_json::json!({
273+
"set": set_value_expr_json
274+
}));
275+
assert!(result.is_ok(), "Failed to deserialize set task with expression: {:?}", result.err());
276+
277+
let set_task = result.unwrap();
278+
match set_task.set {
279+
SetValue::Expression(expr) => {
280+
assert_eq!(expr, "${ $workflow.input[0] }");
281+
}
282+
SetValue::Map(_) => {
283+
panic!("Expected SetValue::Expression but got SetValue::Map");
284+
}
285+
}
286+
}
287+
288+
#[test]
289+
fn test_wait_task_iso8601_deserialization() {
290+
// Test WaitTask with ISO 8601 duration string
291+
let wait_task_json = serde_json::json!({
292+
"wait": "PT30S"
293+
});
294+
let result: Result<WaitTaskDefinition, _> = serde_json::from_value(wait_task_json);
295+
assert!(result.is_ok(), "Failed to deserialize wait task with ISO 8601: {:?}", result.err());
296+
297+
let wait_task = result.unwrap();
298+
match wait_task.wait {
299+
OneOfDurationOrIso8601Expression::Iso8601Expression(expr) => {
300+
assert_eq!(expr, "PT30S");
301+
}
302+
OneOfDurationOrIso8601Expression::Duration(_) => {
303+
panic!("Expected Iso8601Expression but got Duration");
304+
}
305+
}
306+
}
307+
308+
#[test]
309+
fn test_wait_task_inline_duration_deserialization() {
310+
// Test WaitTask with inline duration properties
311+
let wait_task_json = serde_json::json!({
312+
"wait": {
313+
"seconds": 30
314+
}
315+
});
316+
let result: Result<WaitTaskDefinition, _> = serde_json::from_value(wait_task_json);
317+
assert!(result.is_ok(), "Failed to deserialize wait task with inline duration: {:?}", result.err());
318+
319+
let wait_task = result.unwrap();
320+
match wait_task.wait {
321+
OneOfDurationOrIso8601Expression::Duration(duration) => {
322+
assert_eq!(duration.seconds, Some(30));
323+
}
324+
OneOfDurationOrIso8601Expression::Iso8601Expression(_) => {
325+
panic!("Expected Duration but got Iso8601Expression");
326+
}
327+
}
328+
}
329+
330+
#[test]
331+
fn test_script_process_arguments_array_deserialization() {
332+
use crate::models::task::ScriptProcessDefinition;
333+
334+
// Test ScriptProcessDefinition with arguments as an array (spec-compliant)
335+
let script_process_json = serde_json::json!({
336+
"language": "javascript",
337+
"code": "console.log('test')",
338+
"arguments": ["hello", "world"]
339+
});
340+
let result: Result<ScriptProcessDefinition, _> = serde_json::from_value(script_process_json);
341+
assert!(result.is_ok(), "Failed to deserialize script with array arguments: {:?}", result.err());
342+
343+
let script = result.unwrap();
344+
assert_eq!(script.language, "javascript");
345+
assert!(script.arguments.is_some());
346+
347+
let args = script.arguments.unwrap();
348+
assert_eq!(args.len(), 2);
349+
assert_eq!(args[0], "hello");
350+
assert_eq!(args[1], "world");
351+
}
352+
353+
#[test]
354+
fn test_script_process_with_stdin_deserialization() {
355+
use crate::models::task::ScriptProcessDefinition;
356+
357+
// Test ScriptProcessDefinition with stdin property
358+
let script_process_json = serde_json::json!({
359+
"language": "python",
360+
"code": "print('test')",
361+
"stdin": "Hello Workflow",
362+
"arguments": ["arg1"],
363+
"environment": {"FOO": "bar"}
364+
});
365+
let result: Result<ScriptProcessDefinition, _> = serde_json::from_value(script_process_json);
366+
assert!(result.is_ok(), "Failed to deserialize script with stdin: {:?}", result.err());
367+
368+
let script = result.unwrap();
369+
assert_eq!(script.language, "python");
370+
assert_eq!(script.stdin, Some("Hello Workflow".to_string()));
371+
assert!(script.arguments.is_some());
372+
assert_eq!(script.arguments.as_ref().unwrap().len(), 1);
373+
assert!(script.environment.is_some());
374+
assert_eq!(script.environment.as_ref().unwrap().get("FOO"), Some(&"bar".to_string()));
375+
}
240376
}

core/src/models/task.rs

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ impl ContainerProcessDefinition {
732732
/// Represents the definition of a script evaluation process
733733
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
734734
pub struct ScriptProcessDefinition{
735-
735+
736736
/// Gets/sets the language of the script to run
737737
#[serde(rename = "language")]
738738
pub language: String,
@@ -745,9 +745,13 @@ pub struct ScriptProcessDefinition{
745745
#[serde(rename = "source", skip_serializing_if = "Option::is_none")]
746746
pub source: Option<ExternalResourceDefinition>,
747747

748-
/// Gets/sets a key/value mapping of the arguments, if any, to pass to the script to run
748+
/// Gets/sets the data to pass to the process via stdin
749+
#[serde(rename = "stdin", skip_serializing_if = "Option::is_none")]
750+
pub stdin: Option<String>,
751+
752+
/// Gets/sets a list of arguments, if any, to pass to the script (argv)
749753
#[serde(rename = "arguments", skip_serializing_if = "Option::is_none")]
750-
pub arguments: Option<HashMap<String, String>>,
754+
pub arguments: Option<Vec<String>>,
751755

752756
/// Gets/sets a key/value mapping of the environment variables, if any, to use when running the configured process
753757
#[serde(rename = "environment", skip_serializing_if = "Option::is_none")]
@@ -757,23 +761,25 @@ pub struct ScriptProcessDefinition{
757761
impl ScriptProcessDefinition {
758762

759763
/// Initializes a new script from code
760-
pub fn from_code(language: &str, code: String, arguments: Option<HashMap<String, String>>, environment: Option<HashMap<String, String>>) -> Self{
761-
Self {
762-
language: language.to_string(),
763-
code: Some(code),
764-
source: None,
765-
arguments,
764+
pub fn from_code(language: &str, code: String, stdin: Option<String>, arguments: Option<Vec<String>>, environment: Option<HashMap<String, String>>) -> Self{
765+
Self {
766+
language: language.to_string(),
767+
code: Some(code),
768+
source: None,
769+
stdin,
770+
arguments,
766771
environment
767772
}
768773
}
769774

770775
/// Initializes a new script from an external resource
771-
pub fn from_source(language: &str, source: ExternalResourceDefinition, arguments: Option<HashMap<String, String>>, environment: Option<HashMap<String, String>>) -> Self{
772-
Self {
773-
language: language.to_string(),
774-
code: None,
775-
source: Some(source),
776-
arguments,
776+
pub fn from_source(language: &str, source: ExternalResourceDefinition, stdin: Option<String>, arguments: Option<Vec<String>>, environment: Option<HashMap<String, String>>) -> Self{
777+
Self {
778+
language: language.to_string(),
779+
code: None,
780+
source: Some(source),
781+
stdin,
782+
arguments,
777783
environment
778784
}
779785
}
@@ -838,13 +844,29 @@ impl WorkflowProcessDefinition {
838844
}
839845
}
840846

847+
/// Represents the value that can be set in a Set task - either a map or a runtime expression string
848+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
849+
#[serde(untagged)]
850+
pub enum SetValue {
851+
/// A map of key-value pairs to set
852+
Map(HashMap<String, Value>),
853+
/// A runtime expression string that evaluates to the data to set
854+
Expression(String),
855+
}
856+
857+
impl Default for SetValue {
858+
fn default() -> Self {
859+
SetValue::Map(HashMap::new())
860+
}
861+
}
862+
841863
/// Represents the definition of a task used to set data
842864
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
843865
pub struct SetTaskDefinition{
844866

845867
/// Gets/sets the data to set
846868
#[serde(rename = "set")]
847-
pub set: HashMap<String, Value>,
869+
pub set: SetValue,
848870

849871
/// Gets/sets the task's common fields
850872
#[serde(flatten)]
@@ -859,8 +881,8 @@ impl TaskDefinitionBase for SetTaskDefinition {
859881
impl SetTaskDefinition {
860882
/// Initializes a new SetTaskDefinition
861883
pub fn new() -> Self{
862-
Self {
863-
set: HashMap::new(),
884+
Self {
885+
set: SetValue::Map(HashMap::new()),
864886
common: TaskDefinitionFields::new()
865887
}
866888
}
@@ -988,8 +1010,8 @@ pub struct ErrorFilterDefinition{
9881010
pub struct WaitTaskDefinition{
9891011

9901012
/// Gets/sets the amount of time to wait before resuming workflow
991-
#[serde(rename = "duration")]
992-
pub duration: OneOfDurationOrIso8601Expression,
1013+
#[serde(rename = "wait")]
1014+
pub wait: OneOfDurationOrIso8601Expression,
9931015

9941016
/// Gets/sets the task's common fields
9951017
#[serde(flatten)]
@@ -1002,11 +1024,11 @@ impl TaskDefinitionBase for WaitTaskDefinition {
10021024
}
10031025
}
10041026
impl WaitTaskDefinition {
1005-
1027+
10061028
/// Initializes a new WaitTaskDefinition
1007-
pub fn new(duration: OneOfDurationOrIso8601Expression) -> Self{
1008-
Self {
1009-
duration,
1029+
pub fn new(wait: OneOfDurationOrIso8601Expression) -> Self{
1030+
Self {
1031+
wait,
10101032
common: TaskDefinitionFields::new()
10111033
}
10121034
}

0 commit comments

Comments
 (0)