Skip to content

Commit

Permalink
Use interpolate keys from common (#14262)
Browse files Browse the repository at this point in the history
  • Loading branch information
nolag authored Aug 28, 2024
1 parent 905cf65 commit 99d9d28
Show file tree
Hide file tree
Showing 12 changed files with 30 additions and 376 deletions.
2 changes: 1 addition & 1 deletion core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/prometheus/client_golang v1.17.0
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chainlink-automation v1.0.4
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828152114-571392f2833a
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7
github.com/spf13/cobra v1.8.0
Expand Down
4 changes: 2 additions & 2 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1186,8 +1186,8 @@ github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8um
github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 h1:LAgJTg9Yr/uCo2g7Krp88Dco2U45Y6sbJVl8uKoLkys=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95/go.mod h1:/ZWraCBaDDgaIN1prixYcbVvIk/6HeED9+8zbWQ+TMo=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 h1:9YHYswxhxMAgdJhb+APrf57ZEPsxML8H71oxxvU36eU=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949/go.mod h1:bE6E7KwB8dkFUWKxJTTTtrNAl9xFPGlurKpDVhRz1tk=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828152114-571392f2833a h1:MA1Lw4ZL8A/xyr5lW5WjM0zgI8ZL1AEfIOOTjZFcZlI=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828152114-571392f2833a/go.mod h1:bE6E7KwB8dkFUWKxJTTTtrNAl9xFPGlurKpDVhRz1tk=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs=
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw=
Expand Down
3 changes: 2 additions & 1 deletion core/services/workflows/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/jonboulle/clockwork"
"github.com/smartcontractkit/chainlink-common/pkg/workflows/exec"

"github.com/smartcontractkit/chainlink-common/pkg/capabilities"
"github.com/smartcontractkit/chainlink-common/pkg/services"
Expand Down Expand Up @@ -726,7 +727,7 @@ func (e *Engine) executeStep(ctx context.Context, msg stepRequest) (*values.Map,
inputs = step.Inputs.Mapping
}

i, err := findAndInterpolateAllKeys(inputs, msg.state)
i, err := exec.FindAndInterpolateAllKeys(inputs, msg.state)
if err != nil {
return nil, nil, err
}
Expand Down
95 changes: 0 additions & 95 deletions core/services/workflows/state.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package workflows

import (
"fmt"
"strconv"
"strings"

"github.com/smartcontractkit/chainlink/v2/core/services/workflows/store"

"github.com/smartcontractkit/chainlink-common/pkg/values"
"github.com/smartcontractkit/chainlink-common/pkg/workflows"
)

// copyState returns a deep copy of the input executionState
Expand Down Expand Up @@ -44,93 +39,3 @@ func copyState(es store.WorkflowExecution) store.WorkflowExecution {
Steps: steps,
}
}

// interpolateKey takes a multi-part, dot-separated key and attempts to replace
// it with its corresponding value in `state`.
//
// A key is valid if it contains at least two parts, with:
// - the first part being the workflow step's `ref` variable
// - the second part being one of `inputs` or `outputs`
//
// If a key has more than two parts, then we traverse the parts
// to find the value we want to replace.
// We support traversing both nested maps and lists and any combination of the two.
func interpolateKey(key string, state store.WorkflowExecution) (any, error) {
parts := strings.Split(key, ".")

if len(parts) < 2 {
return "", fmt.Errorf("cannot interpolate %s: must have at least two parts", key)
}

// lookup the step we want to get either input or output state from
sc, ok := state.Steps[parts[0]]
if !ok {
return "", fmt.Errorf("could not find ref `%s`", parts[0])
}

var value values.Value
switch parts[1] {
case "inputs":
value = sc.Inputs
case "outputs":
if sc.Outputs.Err != nil {
return "", fmt.Errorf("cannot interpolate ref part `%s` in `%+v`: step has errored", parts[1], sc)
}

value = sc.Outputs.Value
default:
return "", fmt.Errorf("cannot interpolate ref part `%s` in `%+v`: second part must be `inputs` or `outputs`", parts[1], sc)
}

val, err := values.Unwrap(value)
if err != nil {
return "", err
}

remainingParts := parts[2:]
for _, r := range remainingParts {
switch v := val.(type) {
case map[string]any:
inner, ok := v[r]
if !ok {
return "", fmt.Errorf("could not find ref part `%s` (ref: `%s`) in `%+v`", r, key, v)
}

val = inner
case []any:
i, err := strconv.Atoi(r)
if err != nil {
return "", fmt.Errorf("could not interpolate ref part `%s` (ref: `%s`) in `%+v`: `%s` is not convertible to an int", r, key, v, r)
}

if (i > len(v)-1) || (i < 0) {
return "", fmt.Errorf("could not interpolate ref part `%s` (ref: `%s`) in `%+v`: index out of bounds %d", r, key, v, i)
}

val = v[i]
default:
return "", fmt.Errorf("could not interpolate ref part `%s` (ref: `%s`) in `%+v`", r, key, val)
}
}

return val, nil
}

// findAndInterpolateAllKeys takes an `input` any value, and recursively
// identifies any values that should be replaced from `state`.
//
// A value `v` should be replaced if it is wrapped as follows: `$(v)`.
func findAndInterpolateAllKeys(input any, state store.WorkflowExecution) (any, error) {
return workflows.DeepMap(
input,
func(el string) (any, error) {
matches := workflows.InterpolationTokenRe.FindStringSubmatch(el)
if len(matches) < 2 {
return el, nil
}

interpolatedVar := matches[1]
return interpolateKey(interpolatedVar, state)
},
)
}
Loading

0 comments on commit 99d9d28

Please sign in to comment.