From d034197092e2e628af392a75a7a95e005dfd2647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Tue, 24 Mar 2020 12:32:06 +0100 Subject: [PATCH] Add support for expression evaluation --- go.mod | 2 +- pkg/migrate/README.adoc | 17 +++++++++++++++++ pkg/migrate/types/eval.go | 25 +++++++++++++++++++++++++ pkg/migrate/types/node.go | 27 +++++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 pkg/migrate/types/eval.go diff --git a/go.mod b/go.mod index b82466513..3d596d0bf 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/766b/chi-prometheus v0.0.0-20180509160047-46ac2b31aa30 github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d github.com/Masterminds/squirrel v1.1.1-0.20191017225151-12f2162c8d8d - github.com/PaesslerAG/gval v1.0.1 // indirect + github.com/PaesslerAG/gval v1.0.1 github.com/PaesslerAG/jsonpath v0.1.1 // indirect github.com/SentimensRG/ctx v0.0.0-20180729130232-0bfd988c655d github.com/crusttech/go-oidc v0.0.0-20180918092017-982855dad3e1 diff --git a/pkg/migrate/README.adoc b/pkg/migrate/README.adoc index 60844a0d0..e421d789a 100644 --- a/pkg/migrate/README.adoc +++ b/pkg/migrate/README.adoc @@ -171,3 +171,20 @@ The following value mapping maps `sys_status` field's values; the left one into } } ---- + +The system also provides support for arbitrary mathematical expressions. +If you wish to perform an expression, prefix the mapped value with `=EVL=`; for example `=EVL=numFmt(cell, \"%.0f\")`. + +Variables: +* current cell -- `cell`. + +The following example will remove the decimal point from every `sys_rating` in the given source. + +[source,json] +---- +{ + "sys_rating": { + "*": "=EVL=numFmt(cell, \"%.0f\")" + } +} +---- diff --git a/pkg/migrate/types/eval.go b/pkg/migrate/types/eval.go new file mode 100644 index 000000000..b2362a378 --- /dev/null +++ b/pkg/migrate/types/eval.go @@ -0,0 +1,25 @@ +package types + +import ( + "fmt" + "strconv" + + "github.com/PaesslerAG/gval" +) + +// generates a simple gval language to be used within the migration +func exprs() gval.Language { + return gval.NewLanguage( + gval.JSON(), + gval.Arithmetic(), + + gval.Function("numFmt", func(number, format string) (string, error) { + nn, err := strconv.ParseFloat(number, 64) + if err != nil { + return "", err + } + + return fmt.Sprintf(format, nn), nil + }), + ) +} diff --git a/pkg/migrate/types/node.go b/pkg/migrate/types/node.go index 2252bc9c5..428e498ff 100644 --- a/pkg/migrate/types/node.go +++ b/pkg/migrate/types/node.go @@ -1,6 +1,7 @@ package types import ( + "context" "encoding/csv" "errors" "fmt" @@ -72,6 +73,10 @@ type ( } ) +const ( + evalPrefix = "=EVL=" +) + // helper, to determine if the two nodes are equal func (n *Node) Compare(to *Node) bool { return n.Name == to.Name && n.spliced == to.spliced @@ -392,6 +397,8 @@ func importNodeSource(n *Node, users map[string]uint64, repo repository.RecordRe return r } + lng := exprs() + for { looper: record, err := n.Reader.Read() @@ -526,10 +533,26 @@ func importNodeSource(n *Node, users map[string]uint64, repo repository.RecordRe for i, v := range values { if fmp, ok := n.ValueMap[h]; ok { + nvl := "" if mpv, ok := fmp[v]; ok { - v = mpv + nvl = mpv } else if mpv, ok := fmp["*"]; ok { - v = mpv + nvl = mpv + } + + if nvl != "" && strings.HasPrefix(nvl, evalPrefix) { + opp := nvl[len(evalPrefix):len(nvl)] + ev, err := lng.NewEvaluable(opp) + if err != nil { + return nil, err + } + + v, err = ev.EvalString(context.Background(), map[string]interface{}{"cell": v}) + if err != nil { + return nil, err + } + } else if nvl != "" { + v = nvl } }