diff --git a/docs/backstage/catalog-info.yaml b/docs/backstage/catalog-info.yaml index 9d01682..23f9d60 100644 --- a/docs/backstage/catalog-info.yaml +++ b/docs/backstage/catalog-info.yaml @@ -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: diff --git a/docs/backstage/pipelines/entries.jsonnet b/docs/backstage/pipelines/entries.jsonnet index daf6492..2834f1d 100644 --- a/docs/backstage/pipelines/entries.jsonnet +++ b/docs/backstage/pipelines/entries.jsonnet @@ -52,10 +52,10 @@ 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: [ @@ -63,7 +63,7 @@ id: 'description', name: 'Description', type: 'Text', - source: '$.metadata.description', + source: '_.get($.metadata, "description", "default description")', }, { id: 'api_type', diff --git a/docs/expressions.md b/docs/expressions.md index a8b7763..ec16c2d 100644 --- a/docs/expressions.md +++ b/docs/expressions.md @@ -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 diff --git a/expr/js_eval.go b/expr/js_eval.go index 533368a..d4b9f95 100644 --- a/expr/js_eval.go +++ b/expr/js_eval.go @@ -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) { @@ -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) diff --git a/expr/js_eval_test.go b/expr/js_eval_test.go index 75471ae..79a70d2 100644 --- a/expr/js_eval_test.go +++ b/expr/js_eval_test.go @@ -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" diff --git a/reconcile/entries.go b/reconcile/entries.go index 1de685d..f243248 100644 --- a/reconcile/entries.go +++ b/reconcile/entries.go @@ -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)