Skip to content

Commit

Permalink
Add underscore to js evaluation to allow for use of get() (#83)
Browse files Browse the repository at this point in the history
This will help with nested fields that could potentially be `null`

---------

Co-authored-by: Liam Hegarty <liam.hegarty@incident.io>
  • Loading branch information
macebake and LHeggy authored Mar 12, 2024
1 parent c6957cd commit 6a4f5e7
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 11 deletions.
1 change: 0 additions & 1 deletion docs/backstage/catalog-info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ apiVersion: backstage.io/v1alpha1
kind: API
metadata:
description: The incident.io public API
name: api-incident-io
namespace: default
spec:
definition:
Expand Down
8 changes: 4 additions & 4 deletions docs/backstage/pipelines/entries.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,18 @@
type_name: 'Custom["BackstageAPI"]',
source: {
filter: '$.apiVersion == "backstage.io/v1alpha1" && $.kind == "API"',
name: '$.metadata.name',
external_id: '$.metadata.namespace + "/" + $.metadata.name',
name: '_.get($.metadata, "name", "default API name")',
external_id: '_.get($.metadata, "namespace", "default description") + "/" + _.get($.metadata, "name", "default API name")',
aliases: [
'$.metadata.name',
'_.get($.name, "name", "default alias")',
],
},
attributes: [
{
id: 'description',
name: 'Description',
type: 'Text',
source: '$.metadata.description',
source: '_.get($.metadata, "description", "default description")',
},
{
id: 'api_type',
Expand Down
11 changes: 11 additions & 0 deletions docs/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ The following expressions would evaluate to:
- `$.runbooks``["https://github.com/incident-io/runbooks/blob/main/website.md"]`
- `$.runbooks[0]``https://github.com/incident-io/runbooks/blob/main/website.md`


You can also use `get` to evaluate nested fields that may be null.
For the example entry above, the following expressions would evaluate to:

- `_.get($.metadata, "name")``null`
- `_.get($.metadata, "name", "default name")``default name`
- `_.get($.details, "alias")``null`
- `_.get($.details, "alias", "default alias")``default alias`
- `_.get($.details, "description")``Marketing website`


## Migrating from CEL

As shown above, the main difference between CEL and JavaScript is that your
Expand Down
23 changes: 18 additions & 5 deletions expr/js_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,23 @@ import (
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/robertkrimen/otto"
underscore "github.com/robertkrimen/otto/underscore"
)

var vm *otto.Otto

func init() {

underscore.Enable()

// Create a Javascript virtual machine that we'll use for evaluating the source
// expression. We must be very careful: this is executing code on behalf of others, so
// comes with all normal warnings.
vm = otto.New()
vm.Interrupt = make(chan func(), 1)

}

// EvaluateJavascript can evaluate a source Javascript program having set the given
// subject into the `$` variable.
func EvaluateJavascript(ctx context.Context, source string, subject any, logger kitlog.Logger) (result otto.Value, err error) {
Expand All @@ -27,11 +42,9 @@ func EvaluateJavascript(ctx context.Context, source string, subject any, logger
}
}()

// Create a Javascript virtual machine that we'll use for evaluating the source
// expression. We must be very careful: this is executing code on behalf of others, so
// comes with all normal warnings.
vm := otto.New()
vm.Interrupt = make(chan func(), 1)
if vm == nil {
panic("Javascript virtual machine not initialised")
}

// Start a new function bounded context.
ctx, cancel := context.WithCancel(ctx)
Expand Down
7 changes: 7 additions & 0 deletions expr/js_eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ var _ = Describe("Javascript evaluation", func() {
Expect(evaluatedResult).To(Equal(sourceEntry["metadata"].(map[string]any)["namespace"]))
})

It("handles possible null values with _.get", func() {
nestedSrc := "_.get($.metadata, \"badKey\", \"default value\")"
evaluatedResult, err := EvaluateSingleValue[string](ctx, nestedSrc, sourceEntry)
Expect(err).NotTo(HaveOccurred())
Expect(evaluatedResult).To(Equal("default value"))
})

When("parsing array values", func() {
It("returns an error if the input is not an array", func() {
topLevelSrc := "$.name"
Expand Down
2 changes: 1 addition & 1 deletion reconcile/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ func Entries(ctx context.Context, logger kitlog.Logger, cl EntriesClient, catalo
}
}

logger.Log("msg", fmt.Sprintf("found %d entries that need updated", len(toUpdate)))
logger.Log("msg", fmt.Sprintf("found %d entries that need updating", len(toUpdate)))

g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)
Expand Down

0 comments on commit 6a4f5e7

Please sign in to comment.