Skip to content

Commit a776b63

Browse files
authored
Update SDK to handle spec changes on run tasks (#250)
* Update run.container for new spec updates Signed-off-by: Simon Emms <[email protected]> * Update run.script for new spec updates Signed-off-by: Simon Emms <[email protected]> * Add RunArguments type to handle object and array inputs Signed-off-by: Simon Emms <[email protected]> --------- Signed-off-by: Simon Emms <[email protected]>
1 parent 3339344 commit a776b63

File tree

2 files changed

+196
-17
lines changed

2 files changed

+196
-17
lines changed

model/task_run.go

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,34 @@ type RunTaskConfiguration struct {
3939

4040
type Container struct {
4141
Image string `json:"image" validate:"required"`
42+
Name string `json:"name,omitempty"`
4243
Command string `json:"command,omitempty"`
4344
Ports map[string]interface{} `json:"ports,omitempty"`
4445
Volumes map[string]interface{} `json:"volumes,omitempty"`
4546
Environment map[string]string `json:"environment,omitempty"`
47+
Input string `json:"stdin,omitempty"`
48+
Arguments []string `json:"arguments,omitempty"`
49+
Lifetime *ContainerLifetime `json:"lifetime,omitempty"`
50+
}
51+
52+
type ContainerLifetime struct {
53+
Cleanup string `json:"cleanup" validate:"required,oneof=always never eventually"`
54+
After *Duration `json:"after" validate:"required_if=Cleanup eventually"`
4655
}
4756

4857
type Script struct {
49-
Language string `json:"language" validate:"required"`
50-
Arguments map[string]interface{} `json:"arguments,omitempty"`
51-
Environment map[string]string `json:"environment,omitempty"`
52-
InlineCode *string `json:"code,omitempty"`
53-
External *ExternalResource `json:"source,omitempty"`
58+
Language string `json:"language" validate:"required,oneof=javascript js python"` // "js" exists for legacy reasons, use "javascript" instead
59+
Arguments *RunArguments `json:"arguments,omitempty"`
60+
Environment map[string]string `json:"environment,omitempty"`
61+
InlineCode *string `json:"code,omitempty"`
62+
External *ExternalResource `json:"source,omitempty"`
63+
Input string `json:"stdin,omitempty"`
5464
}
5565

5666
type Shell struct {
57-
Command string `json:"command" validate:"required"`
58-
Arguments map[string]interface{} `json:"arguments,omitempty"`
59-
Environment map[string]string `json:"environment,omitempty"`
67+
Command string `json:"command" validate:"required"`
68+
Arguments *RunArguments `json:"arguments,omitempty"`
69+
Environment map[string]string `json:"environment,omitempty"`
6070
}
6171

6272
type RunWorkflow struct {
@@ -126,3 +136,46 @@ func (rtc *RunTaskConfiguration) MarshalJSON() ([]byte, error) {
126136

127137
return json.Marshal(temp)
128138
}
139+
140+
type RunArguments struct {
141+
Value any `json:"-"`
142+
}
143+
144+
func (a *RunArguments) MarshalJSON() ([]byte, error) {
145+
switch v := a.Value.(type) {
146+
case map[string]interface{}, []string:
147+
return json.Marshal(v)
148+
default:
149+
return nil, errors.New("unknown RunArguments type")
150+
}
151+
}
152+
153+
func (a *RunArguments) UnmarshalJSON(data []byte) error {
154+
var m map[string]interface{}
155+
if err := json.Unmarshal(data, &m); err == nil {
156+
a.Value = m
157+
return nil
158+
}
159+
160+
var s []string
161+
if err := json.Unmarshal(data, &s); err == nil {
162+
a.Value = s
163+
return nil
164+
}
165+
166+
return errors.New("data must be a valid array of strings or object map")
167+
}
168+
169+
func (a *RunArguments) AsMap() map[string]interface{} {
170+
if v, ok := a.Value.(map[string]interface{}); ok {
171+
return v
172+
}
173+
return nil
174+
}
175+
176+
func (a *RunArguments) AsSlice() []string {
177+
if v, ok := a.Value.([]string); ok {
178+
return v
179+
}
180+
return nil
181+
}

model/task_run_test.go

Lines changed: 135 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,23 @@ func TestRunTask_MarshalJSON(t *testing.T) {
3737
Await: boolPtr(true),
3838
Container: &Container{
3939
Image: "example-image",
40+
Name: "example-name",
4041
Command: "example-command",
4142
Ports: map[string]interface{}{
4243
"8080": "80",
4344
},
4445
Environment: map[string]string{
4546
"ENV_VAR": "value",
4647
},
48+
Input: "example-input",
49+
Arguments: []string{
50+
"arg1",
51+
"arg2",
52+
},
53+
Lifetime: &ContainerLifetime{
54+
Cleanup: "eventually",
55+
After: NewDurationExpr("20s"),
56+
},
4757
},
4858
},
4959
}
@@ -61,9 +71,16 @@ func TestRunTask_MarshalJSON(t *testing.T) {
6171
"await": true,
6272
"container": {
6373
"image": "example-image",
74+
"name": "example-name",
6475
"command": "example-command",
6576
"ports": {"8080": "80"},
66-
"environment": {"ENV_VAR": "value"}
77+
"environment": {"ENV_VAR": "value"},
78+
"stdin": "example-input",
79+
"arguments": ["arg1","arg2"],
80+
"lifetime": {
81+
"cleanup": "eventually",
82+
"after": "20s"
83+
}
6784
}
6885
}
6986
}`, string(data))
@@ -81,9 +98,18 @@ func TestRunTask_UnmarshalJSON(t *testing.T) {
8198
"await": true,
8299
"container": {
83100
"image": "example-image",
101+
"name": "example-name",
84102
"command": "example-command",
85103
"ports": {"8080": "80"},
86-
"environment": {"ENV_VAR": "value"}
104+
"environment": {"ENV_VAR": "value"},
105+
"stdin": "example-input",
106+
"arguments": ["arg1","arg2"],
107+
"lifetime": {
108+
"cleanup": "eventually",
109+
"after": {
110+
"seconds": 20
111+
}
112+
}
87113
}
88114
}
89115
}`
@@ -102,9 +128,14 @@ func TestRunTask_UnmarshalJSON(t *testing.T) {
102128
assert.Equal(t, "example-command", runTask.Run.Container.Command)
103129
assert.Equal(t, map[string]interface{}{"8080": "80"}, runTask.Run.Container.Ports)
104130
assert.Equal(t, map[string]string{"ENV_VAR": "value"}, runTask.Run.Container.Environment)
131+
assert.Equal(t, "example-name", runTask.Run.Container.Name)
132+
assert.Equal(t, "example-input", runTask.Run.Container.Input)
133+
assert.Equal(t, []string{"arg1", "arg2"}, runTask.Run.Container.Arguments)
134+
assert.Equal(t, "eventually", runTask.Run.Container.Lifetime.Cleanup)
135+
assert.Equal(t, &DurationInline{Seconds: 20}, runTask.Run.Container.Lifetime.After.AsInline())
105136
}
106137

107-
func TestRunTaskScript_MarshalJSON(t *testing.T) {
138+
func TestRunTaskScriptArgsMap_MarshalJSON(t *testing.T) {
108139
runTask := RunTask{
109140
TaskBase: TaskBase{
110141
If: &RuntimeExpression{Value: "${condition}"},
@@ -120,13 +151,16 @@ func TestRunTaskScript_MarshalJSON(t *testing.T) {
120151
Await: boolPtr(true),
121152
Script: &Script{
122153
Language: "python",
123-
Arguments: map[string]interface{}{
124-
"arg1": "value1",
154+
Arguments: &RunArguments{
155+
Value: map[string]interface{}{
156+
"arg1": "value1",
157+
},
125158
},
126159
Environment: map[string]string{
127160
"ENV_VAR": "value",
128161
},
129162
InlineCode: stringPtr("print('Hello, World!')"),
163+
Input: "example-input",
130164
},
131165
},
132166
}
@@ -146,13 +180,14 @@ func TestRunTaskScript_MarshalJSON(t *testing.T) {
146180
"language": "python",
147181
"arguments": {"arg1": "value1"},
148182
"environment": {"ENV_VAR": "value"},
149-
"code": "print('Hello, World!')"
183+
"code": "print('Hello, World!')",
184+
"stdin": "example-input"
150185
}
151186
}
152187
}`, string(data))
153188
}
154189

155-
func TestRunTaskScript_UnmarshalJSON(t *testing.T) {
190+
func TestRunTaskScriptArgsMap_UnmarshalJSON(t *testing.T) {
156191
jsonData := `{
157192
"if": "${condition}",
158193
"input": { "from": {"key": "value"} },
@@ -166,7 +201,97 @@ func TestRunTaskScript_UnmarshalJSON(t *testing.T) {
166201
"language": "python",
167202
"arguments": {"arg1": "value1"},
168203
"environment": {"ENV_VAR": "value"},
169-
"code": "print('Hello, World!')"
204+
"code": "print('Hello, World!')",
205+
"stdin": "example-input"
206+
}
207+
}
208+
}`
209+
210+
var runTask RunTask
211+
err := json.Unmarshal([]byte(jsonData), &runTask)
212+
assert.NoError(t, err)
213+
assert.Equal(t, &RuntimeExpression{Value: "${condition}"}, runTask.If)
214+
assert.Equal(t, &Input{From: &ObjectOrRuntimeExpr{Value: map[string]interface{}{"key": "value"}}}, runTask.Input)
215+
assert.Equal(t, &Output{As: &ObjectOrRuntimeExpr{Value: map[string]interface{}{"result": "output"}}}, runTask.Output)
216+
assert.Equal(t, &TimeoutOrReference{Timeout: &Timeout{After: NewDurationExpr("10s")}}, runTask.Timeout)
217+
assert.Equal(t, &FlowDirective{Value: "continue"}, runTask.Then)
218+
assert.Equal(t, map[string]interface{}{"meta": "data"}, runTask.Metadata)
219+
assert.Equal(t, true, *runTask.Run.Await)
220+
assert.Equal(t, "python", runTask.Run.Script.Language)
221+
assert.Equal(t, map[string]interface{}{"arg1": "value1"}, runTask.Run.Script.Arguments.AsMap())
222+
assert.Equal(t, map[string]string{"ENV_VAR": "value"}, runTask.Run.Script.Environment)
223+
assert.Equal(t, "print('Hello, World!')", *runTask.Run.Script.InlineCode)
224+
assert.Equal(t, "example-input", runTask.Run.Script.Input)
225+
}
226+
227+
func TestRunTaskScriptArgArray_MarshalJSON(t *testing.T) {
228+
runTask := RunTask{
229+
TaskBase: TaskBase{
230+
If: &RuntimeExpression{Value: "${condition}"},
231+
Input: &Input{From: &ObjectOrRuntimeExpr{Value: map[string]interface{}{"key": "value"}}},
232+
Output: &Output{As: &ObjectOrRuntimeExpr{Value: map[string]interface{}{"result": "output"}}},
233+
Timeout: &TimeoutOrReference{Timeout: &Timeout{After: NewDurationExpr("10s")}},
234+
Then: &FlowDirective{Value: "continue"},
235+
Metadata: map[string]interface{}{
236+
"meta": "data",
237+
},
238+
},
239+
Run: RunTaskConfiguration{
240+
Await: boolPtr(true),
241+
Script: &Script{
242+
Language: "python",
243+
Arguments: &RunArguments{
244+
Value: []string{
245+
"arg1=value1",
246+
},
247+
},
248+
Environment: map[string]string{
249+
"ENV_VAR": "value",
250+
},
251+
InlineCode: stringPtr("print('Hello, World!')"),
252+
Input: "example-input",
253+
},
254+
},
255+
}
256+
257+
data, err := json.Marshal(runTask)
258+
assert.NoError(t, err)
259+
assert.JSONEq(t, `{
260+
"if": "${condition}",
261+
"input": { "from": {"key": "value"} },
262+
"output": { "as": {"result": "output"} },
263+
"timeout": { "after": "10s" },
264+
"then": "continue",
265+
"metadata": {"meta": "data"},
266+
"run": {
267+
"await": true,
268+
"script": {
269+
"language": "python",
270+
"arguments": ["arg1=value1"],
271+
"environment": {"ENV_VAR": "value"},
272+
"code": "print('Hello, World!')",
273+
"stdin": "example-input"
274+
}
275+
}
276+
}`, string(data))
277+
}
278+
279+
func TestRunTaskScriptArgsArray_UnmarshalJSON(t *testing.T) {
280+
jsonData := `{
281+
"if": "${condition}",
282+
"input": { "from": {"key": "value"} },
283+
"output": { "as": {"result": "output"} },
284+
"timeout": { "after": "10s" },
285+
"then": "continue",
286+
"metadata": {"meta": "data"},
287+
"run": {
288+
"await": true,
289+
"script": {
290+
"language": "python",
291+
"arguments": ["arg1=value1"],
292+
"environment": {"ENV_VAR": "value"},
293+
"code": "print('Hello, World!')",
294+
"stdin": "example-input"
170295
}
171296
}
172297
}`
@@ -182,9 +307,10 @@ func TestRunTaskScript_UnmarshalJSON(t *testing.T) {
182307
assert.Equal(t, map[string]interface{}{"meta": "data"}, runTask.Metadata)
183308
assert.Equal(t, true, *runTask.Run.Await)
184309
assert.Equal(t, "python", runTask.Run.Script.Language)
185-
assert.Equal(t, map[string]interface{}{"arg1": "value1"}, runTask.Run.Script.Arguments)
310+
assert.Equal(t, []string{"arg1=value1"}, runTask.Run.Script.Arguments.AsSlice())
186311
assert.Equal(t, map[string]string{"ENV_VAR": "value"}, runTask.Run.Script.Environment)
187312
assert.Equal(t, "print('Hello, World!')", *runTask.Run.Script.InlineCode)
313+
assert.Equal(t, "example-input", runTask.Run.Script.Input)
188314
}
189315

190316
func boolPtr(b bool) *bool {

0 commit comments

Comments
 (0)