Skip to content

Commit 9dd0854

Browse files
m1r4cm1r4cChristopherHX
authored
Added support for dereferenced map properties (#2635)
* Added support for dereferenced properties * Added negative test * Update pkg/exprparser/functions_test.go Co-authored-by: ChristopherHX <christopher.homberger@web.de> * Update pkg/exprparser/functions_test.go Co-authored-by: ChristopherHX <christopher.homberger@web.de> * fix lint --------- Co-authored-by: m1r4c <lars-github@domesjo.com> Co-authored-by: ChristopherHX <christopher.homberger@web.de>
1 parent 7c45ad6 commit 9dd0854

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

pkg/exprparser/functions_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ func TestFunctionContains(t *testing.T) {
3131
{`contains(fromJSON('[3.14,"second"]'), 3.14) }}`, true, "contains-item-number-number"},
3232
{`contains(fromJSON('["","second"]'), fromJSON('[]')) }}`, false, "contains-item-str-arr"},
3333
{`contains(fromJSON('["","second"]'), fromJSON('{}')) }}`, false, "contains-item-str-obj"},
34+
{`contains(fromJSON('[{ "first": { "result": "success" }},{ "second": { "result": "success" }}]').first.result, 'success') }}`, true, "multiple-contains-item"},
35+
{`contains(fromJSON('[{ "result": "success" },{ "result": "failure" }]').*.result, 'failure') }}`, true, "multiple-contains-dereferenced-failure-item"},
36+
{`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'success') }}`, true, "multiple-contains-dereferenced-success-item"},
37+
{`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'notthere') }}`, false, "multiple-contains-dereferenced-missing-item"},
38+
{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val1') }}`, true, "multiple-contains-dereferenced-output-item"},
39+
{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val2') }}`, true, "multiple-contains-dereferenced-output-item-2"},
40+
{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'missing') }}`, false, "multiple-contains-dereferenced-output-misssing-item"},
3441
}
3542

3643
env := &EvaluationEnvironment{}
@@ -249,3 +256,23 @@ func TestFunctionFormat(t *testing.T) {
249256
})
250257
}
251258
}
259+
260+
func TestMapContains(t *testing.T) {
261+
env := &EvaluationEnvironment{
262+
Needs: map[string]Needs{
263+
"first-job": {
264+
Outputs: map[string]string{},
265+
Result: "success",
266+
},
267+
"second-job": {
268+
Outputs: map[string]string{},
269+
Result: "failure",
270+
},
271+
},
272+
}
273+
274+
output, err := NewInterpeter(env, Config{}).Evaluate("contains(needs.*.result, 'failure')", DefaultStatusCheckNone)
275+
assert.Nil(t, err)
276+
277+
assert.Equal(t, true, output)
278+
}

pkg/exprparser/interpreter.go

+32
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.Obj
229229
return nil, err
230230
}
231231

232+
_, receiverIsDeref := objectDerefNode.Receiver.(*actionlint.ArrayDerefNode)
233+
if receiverIsDeref {
234+
return impl.getPropertyValueDereferenced(reflect.ValueOf(left), objectDerefNode.Property)
235+
}
232236
return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property)
233237
}
234238

@@ -312,6 +316,34 @@ func (impl *interperterImpl) getPropertyValue(left reflect.Value, property strin
312316
return nil, nil
313317
}
314318

319+
func (impl *interperterImpl) getPropertyValueDereferenced(left reflect.Value, property string) (value interface{}, err error) {
320+
switch left.Kind() {
321+
case reflect.Ptr:
322+
return impl.getPropertyValue(left, property)
323+
324+
case reflect.Struct:
325+
return impl.getPropertyValue(left, property)
326+
case reflect.Map:
327+
iter := left.MapRange()
328+
329+
var values []interface{}
330+
for iter.Next() {
331+
value, err := impl.getPropertyValue(iter.Value(), property)
332+
if err != nil {
333+
return nil, err
334+
}
335+
336+
values = append(values, value)
337+
}
338+
339+
return values, nil
340+
case reflect.Slice:
341+
return impl.getPropertyValue(left, property)
342+
}
343+
344+
return nil, nil
345+
}
346+
315347
func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, error) {
316348
if value.Kind() == reflect.Ptr {
317349
return impl.getMapValue(value.Elem())

pkg/exprparser/interpreter_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,8 @@ func TestContexts(t *testing.T) {
562562
{"matrix.os", "Linux", "matrix-context"},
563563
{"needs.job-id.outputs.output-name", "value", "needs-context"},
564564
{"needs.job-id.result", "success", "needs-context"},
565+
{"contains(needs.*.result, 'success')", true, "needs-wildcard-context-contains-success"},
566+
{"contains(needs.*.result, 'failure')", false, "needs-wildcard-context-contains-failure"},
565567
{"inputs.name", "value", "inputs-context"},
566568
}
567569

@@ -610,6 +612,12 @@ func TestContexts(t *testing.T) {
610612
},
611613
Result: "success",
612614
},
615+
"another-job-id": {
616+
Outputs: map[string]string{
617+
"output-name": "value",
618+
},
619+
Result: "success",
620+
},
613621
},
614622
Inputs: map[string]interface{}{
615623
"name": "value",

0 commit comments

Comments
 (0)