mirror of
https://code.forgejo.org/forgejo/runner.git
synced 2025-08-16 18:01:34 +00:00
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> (cherry picked from commit 20bb25432dd8db44c689a3bc2d440a648e71b3bb)
This commit is contained in:
parent
a737f197c7
commit
2496f8d41c
3 changed files with 67 additions and 0 deletions
|
@ -31,6 +31,13 @@ func TestFunctionContains(t *testing.T) {
|
||||||
{`contains(fromJSON('[3.14,"second"]'), 3.14) }}`, true, "contains-item-number-number"},
|
{`contains(fromJSON('[3.14,"second"]'), 3.14) }}`, true, "contains-item-number-number"},
|
||||||
{`contains(fromJSON('["","second"]'), fromJSON('[]')) }}`, false, "contains-item-str-arr"},
|
{`contains(fromJSON('["","second"]'), fromJSON('[]')) }}`, false, "contains-item-str-arr"},
|
||||||
{`contains(fromJSON('["","second"]'), fromJSON('{}')) }}`, false, "contains-item-str-obj"},
|
{`contains(fromJSON('["","second"]'), fromJSON('{}')) }}`, false, "contains-item-str-obj"},
|
||||||
|
{`contains(fromJSON('[{ "first": { "result": "success" }},{ "second": { "result": "success" }}]').first.result, 'success') }}`, true, "multiple-contains-item"},
|
||||||
|
{`contains(fromJSON('[{ "result": "success" },{ "result": "failure" }]').*.result, 'failure') }}`, true, "multiple-contains-dereferenced-failure-item"},
|
||||||
|
{`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'success') }}`, true, "multiple-contains-dereferenced-success-item"},
|
||||||
|
{`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'notthere') }}`, false, "multiple-contains-dereferenced-missing-item"},
|
||||||
|
{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val1') }}`, true, "multiple-contains-dereferenced-output-item"},
|
||||||
|
{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val2') }}`, true, "multiple-contains-dereferenced-output-item-2"},
|
||||||
|
{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'missing') }}`, false, "multiple-contains-dereferenced-output-misssing-item"},
|
||||||
}
|
}
|
||||||
|
|
||||||
env := &EvaluationEnvironment{}
|
env := &EvaluationEnvironment{}
|
||||||
|
@ -270,3 +277,23 @@ func TestFunctionFormat(t *testing.T) {
|
||||||
_, err := NewInterpeter(env, Config{}).Evaluate("format()", DefaultStatusCheckNone)
|
_, err := NewInterpeter(env, Config{}).Evaluate("format()", DefaultStatusCheckNone)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapContains(t *testing.T) {
|
||||||
|
env := &EvaluationEnvironment{
|
||||||
|
Needs: map[string]Needs{
|
||||||
|
"first-job": {
|
||||||
|
Outputs: map[string]string{},
|
||||||
|
Result: "success",
|
||||||
|
},
|
||||||
|
"second-job": {
|
||||||
|
Outputs: map[string]string{},
|
||||||
|
Result: "failure",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := NewInterpeter(env, Config{}).Evaluate("contains(needs.*.result, 'failure')", DefaultStatusCheckNone)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, true, output)
|
||||||
|
}
|
||||||
|
|
|
@ -233,6 +233,10 @@ func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.Obj
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, receiverIsDeref := objectDerefNode.Receiver.(*actionlint.ArrayDerefNode)
|
||||||
|
if receiverIsDeref {
|
||||||
|
return impl.getPropertyValueDereferenced(reflect.ValueOf(left), objectDerefNode.Property)
|
||||||
|
}
|
||||||
return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property)
|
return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +320,34 @@ func (impl *interperterImpl) getPropertyValue(left reflect.Value, property strin
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (impl *interperterImpl) getPropertyValueDereferenced(left reflect.Value, property string) (value interface{}, err error) {
|
||||||
|
switch left.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return impl.getPropertyValue(left, property)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
return impl.getPropertyValue(left, property)
|
||||||
|
case reflect.Map:
|
||||||
|
iter := left.MapRange()
|
||||||
|
|
||||||
|
var values []interface{}
|
||||||
|
for iter.Next() {
|
||||||
|
value, err := impl.getPropertyValue(iter.Value(), property)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
values = append(values, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return values, nil
|
||||||
|
case reflect.Slice:
|
||||||
|
return impl.getPropertyValue(left, property)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, error) {
|
func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, error) {
|
||||||
if value.Kind() == reflect.Ptr {
|
if value.Kind() == reflect.Ptr {
|
||||||
return impl.getMapValue(value.Elem())
|
return impl.getMapValue(value.Elem())
|
||||||
|
|
|
@ -562,6 +562,8 @@ func TestContexts(t *testing.T) {
|
||||||
{"matrix.os", "Linux", "matrix-context"},
|
{"matrix.os", "Linux", "matrix-context"},
|
||||||
{"needs.job-id.outputs.output-name", "value", "needs-context"},
|
{"needs.job-id.outputs.output-name", "value", "needs-context"},
|
||||||
{"needs.job-id.result", "success", "needs-context"},
|
{"needs.job-id.result", "success", "needs-context"},
|
||||||
|
{"contains(needs.*.result, 'success')", true, "needs-wildcard-context-contains-success"},
|
||||||
|
{"contains(needs.*.result, 'failure')", false, "needs-wildcard-context-contains-failure"},
|
||||||
{"inputs.name", "value", "inputs-context"},
|
{"inputs.name", "value", "inputs-context"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,6 +612,12 @@ func TestContexts(t *testing.T) {
|
||||||
},
|
},
|
||||||
Result: "success",
|
Result: "success",
|
||||||
},
|
},
|
||||||
|
"another-job-id": {
|
||||||
|
Outputs: map[string]string{
|
||||||
|
"output-name": "value",
|
||||||
|
},
|
||||||
|
Result: "success",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Inputs: map[string]interface{}{
|
Inputs: map[string]interface{}{
|
||||||
"name": "value",
|
"name": "value",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue