Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSONPath Support for TriggerBindings #241

Merged
merged 2 commits into from
Dec 10, 2019
Merged

Conversation

dibyom
Copy link
Member

@dibyom dibyom commented Nov 27, 2019

Changes

There are two commits in this PR. The first commit adds functions for JSONPath and the second one uses those functions to extract values for EventBinding.

Commits:
This commit adds functions to support JSONPath
in TriggerBindings using the k8s.io/client-go/util/jsonpath
library.

There are a few deviations from the default behavior of the library:

  1. The JSONPath expressions have to be wrapped in the Tekton variable
    interpolation syntax i.e $() : $(body).

  2. We support the RelaxedJSONPathExpresion syntax used in
    kubectl get -o custom-columns. This means that the curly braces {}
    and the leading dot . can be omitted i.e we support both $(body.key)
    as well as `$({.body.key})

  3. We return valid JSON values when the value is a JSON array or
    map. By default, the library returns a string containing an internal
    go representation. So, if the JSONPath expression selects {"a": "b"},
    the library will return map[a: b] while we return the valid JSON
    string i.e {"a":"b"}

This commit switches TriggerBinding params to use
JSONPath instead of GJSON.

Part of #178

Submitter Checklist

These are the criteria that every PR should meet, please check them off as you
review them:

See the contribution guide for more details.

Release Notes

TriggerBindings now support JSONPath expressions instead of GJSON. Check docs/triggerbindings.md for the syntax and examples

@tekton-robot tekton-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Nov 27, 2019
@tekton-robot tekton-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Nov 27, 2019
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 91.3% -7.3
pkg/template/jsonpath.go Do not exist 77.6%

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 91.3% -7.3
pkg/template/jsonpath.go Do not exist 86.5%

@tekton-robot tekton-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Dec 3, 2019
@dibyom dibyom marked this pull request as ready for review December 3, 2019 00:14
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 89.3% -9.3
pkg/template/jsonpath.go Do not exist 87.3%

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 89.3% -9.3
pkg/template/jsonpath.go Do not exist 87.3%

@dibyom dibyom changed the title WIP: Jsonpath support JSONPath Support for TriggerBindings Dec 3, 2019
@tekton-robot tekton-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 3, 2019
@dibyom dibyom requested a review from wlynch December 3, 2019 00:20
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 89.3% -9.3
pkg/template/jsonpath.go Do not exist 87.3%

pkg/template/event.go Outdated Show resolved Hide resolved
pkg/template/event.go Outdated Show resolved Hide resolved
pkg/template/event.go Outdated Show resolved Hide resolved
pkg/template/event.go Show resolved Hide resolved
pkg/template/event.go Show resolved Hide resolved
pkg/template/jsonpath.go Outdated Show resolved Hide resolved
pkg/template/jsonpath.go Show resolved Hide resolved
pkg/template/jsonpath.go Outdated Show resolved Hide resolved
tests := []struct {
name string
expr string
in string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of encoding the object body to a string then unmarshalling it out, would it be easier to make this interface{} and set the fields as maps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, don't have a strong opinion on this -> might be a bit more verbose for the array and objects that contain other embedded objects?

name: "null values",
in: objectBody,
expr: "$(body.null)",
want: "null",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this WAI? Even though the payload says null we should probably make a distinction between null and "null". I say either default to "" or have ParseJSONPath return *string so we can represent this nil value (my guess is "" is probably sufficient)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so I went back and forth on this a few times before settling on "null". I agree that if we were talking about return go values, the correct way should be *string. However, for our use case here (i.e. concatenating the json values in string in the trigger templates), that would result in the string <nil>. If we use "" to represent the printable value of null, we have no way to differentiate between null and the empty string "".

The "null" and null collision is unfortunate though I think this is the least bad way at the moment. Also, this issue is only valid if the JSONPath selects a field whose value is null. If the selection is an object or an array which contains both the string null as well as a null value, we get the following: {"stringNull":"null","nullValue":null}

@tekton-robot tekton-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Dec 3, 2019
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 89.3% -9.3
pkg/template/jsonpath.go Do not exist 87.3%

@tekton-robot tekton-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Dec 4, 2019
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 89.3% -9.3
pkg/template/jsonpath.go Do not exist 88.5%

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/template/event.go 98.6% 89.3% -9.3
pkg/template/jsonpath.go Do not exist 88.5%

Copy link
Collaborator

@bobcatfish bobcatfish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My biggest overall piece of feedback is that there are some new functions added which could benefit from unit tests. I'm sure we have some coverage via Test_NewResources, and i can see that you want to break that up later on, but in the meantime i think it's still worth adding unit tests for the new functions right away (and its easier to tell what the expected behaviour is in each function, as well as what's covered and what isnt) <-- i think mostly you've done a great job, e.g. unit tests for ParseJSONPath but there are couple of functions which i think could also use tests which you probably skipped b/c they are unexported, and i think many of the functions (e.g. relaxedJSONPathExpression) are significant enough to actually be exported and have their own tests

Other than that I think I've just got nitpicks! Overall looks great :D
/approve

And definitely we want to make sure we squash some of these commits before we merge so:
/hold

`\$\(header(\.[[:alnum:]_\-]+)?\)`
TriggerBindings can access values from the HTTP JSON body and the headers using
JSONPath expressions. The expressions have to be wrapped in `$()` but can omit
the curly braces `{}` and the leading `.`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taking a quick look online, it seems to me like most examples for jsonpath don't include {} anyway? e.g. https://support.smartbear.com/alertsite/docs/monitors/api/endpoint/jsonpath.html and this json path expression tester: https://jsonpath.curiousconcept.com/

so i'm wondering if a) we need to say this at all or if b) this b/c of the specific version of jsonpath we're using, in which case maybe this sentence could explain a bit more with a link, e.g. something like "We are using jsonpath version blah blah blah, and it expects expressions to be wrapped in {}, however we do not require {} in addition to $() though it can be used.."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://kubernetes.io/docs/reference/kubectl/jsonpath/ mentions the enclosing {} though it also says its a JSONPath "template"....maybe we keep things simple and never mention the {}?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the curly braces from the examples!

pkg/template/event.go Outdated Show resolved Hide resolved
pkg/template/event_test.go Outdated Show resolved Hide resolved
}
return resources
}

// event represents a HTTP event that Triggers processes
type event struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe "request" would be a more specific name than "event"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be confusing with http.Request. Also, I like event since we can later add the one-offs such as the $(uid) to this struct to unify how we do the "templating"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be confusing with http.Request

it basically is an http.Request tho since it has a header + a body 🤔

I see what you mean about the uid tho! you're probably right about event being better in the long run, sgtm :D


return &event{
Header: joinedHeaders,
Body: data,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(body) == 0, what does data end up being?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(maybe we handle this totally fine - but to be sure it might be good to have some unit tests directly for newEvent?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i cant see that link anymore - good ol rebasing 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤕

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,158 @@
package template
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm do you think there's another name we could use besides "template"? in pipelines we are trying to avoid using the term "templating" for the variable substitution we are doing b/c "template" implies additional functionality we want to avoid adding

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i just realized the type is literally called "trigger template" and this package is probably named after that so just ignore me 🤦‍♀️

Copy link
Member Author

@dibyom dibyom Dec 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the discussion around JSONPath support in pipeline params, it might be worth having a common jsonpath pkg that both pipeline and triggers could use 🤔

Though that's a yak that we should shave another day 😄

pkg/template/jsonpath_test.go Outdated Show resolved Hide resolved
pkg/template/resource.go Outdated Show resolved Hide resolved
pkg/template/jsonpath.go Outdated Show resolved Hide resolved
pkg/template/jsonpath.go Show resolved Hide resolved
@tekton-robot tekton-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 9, 2019
@tekton-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bobcatfish

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tekton-robot tekton-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 9, 2019
@dibyom
Copy link
Member Author

dibyom commented Dec 9, 2019

i think mostly you've done a great job, e.g. unit tests for ParseJSONPath but there are couple of functions which i think could also use tests which you probably skipped b/c they are unexported, and i think many of the functions (e.g. relaxedJSONPathExpression) are significant enough to actually be exported and have their own tests

The current TektonJSONPathExpression tests does test all the behaviors of RelaxedJSONPath expression. I do see the point about the behavior being easier to grok if some of the unexported functions have their own tests though I'm not sure if we should be exporting functions solely because they are complex or for testing.

Edit: Looking through the coverage report, there are a few error cases that we should add tests for:

      if i != len(results)-1 {
              text = append(text, ' ')
      }


        if !ok {
                return nil, fmt.Errorf("can't print type %s", v.Type())
        }

        if len(submatches) != 3 {
                return "", fmt.Errorf("unexpected submatch list: %v", submatches)
        }



@dibyom dibyom force-pushed the jsonpath branch 3 times, most recently from cc3b2f6 to 4b3dd7f Compare December 10, 2019 16:25
@dibyom
Copy link
Member Author

dibyom commented Dec 10, 2019

/test pull-tekton-triggers-build-tests

In tektoncd#178 we decided to support JSONPath syntax for
expressions in TriggerBindings. This commit adds
functions to support JSONPath using the
`k8s.io/client-go/util/jsonpath` library.

There are a few deviations from the default behavior of the library:

1. The JSONPath expressions have to be wrapped in the Tekton variable
interpolation syntax i.e `$()` : `$(body)`.

2. We  support the `RelaxedJSONPathExpresion` syntax used in
`kubectl get -o custom-columns`. This means that the curly braces `{}`
and the leading dot `.` can be omitted i.e we support both `$(body.key)`
as well as `$({.body.key})

3. We return valid JSON values when the value is a JSON array or
map. By default, the library returns a string containing an internal
go representation. So, if the JSONPath expression selects `{"a": "b"}`,
the library will return `map[a: b]` while we return the valid JSON
string i.e `{"a":"b"}`

In order to support 3, we have to copy a couple of unexported functions
from the library (printResults, and evalToText).

Signed-off-by: Dibyo Mukherjee <[email protected]>
@dibyom dibyom force-pushed the jsonpath branch 2 times, most recently from ec6a539 to 239553c Compare December 10, 2019 19:15
@dibyom
Copy link
Member Author

dibyom commented Dec 10, 2019

Ok, I think I addressed all the comments!

Also, added a few more tests (header values, more relaxed jsonpath expressions etc.)

There area a couple of if branches that are hard to test e.g. we only reach there if the unmarshalled value is a Function or a Channel --I don't think any HTTP JSON payload can be unmarshalled to that!

Quite a few followups from this:

/hold cancel

@tekton-robot tekton-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 10, 2019
@bobcatfish
Copy link
Collaborator

Thanks for all the follow up issues @dibyom !! ❤️

This commit switches the expression syntax in
TriggerBindings from GJson to JSONPath.

Fixes tektoncd#178

Signed-off-by: Dibyo Mukherjee <[email protected]>
@bobcatfish
Copy link
Collaborator

/lgtm

@tekton-robot tekton-robot added the lgtm Indicates that a PR is ready to be merged. label Dec 10, 2019
@bobcatfish
Copy link
Collaborator

/meow space

@tekton-robot
Copy link

@bobcatfish: cat image

In response to this:

/meow space

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@tekton-robot tekton-robot merged commit 89813de into tektoncd:master Dec 10, 2019
@dibyom dibyom deleted the jsonpath branch December 10, 2019 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants