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

HaveJSONPath matcher proposal #637

Open
matt-simons opened this issue Feb 6, 2023 · 3 comments
Open

HaveJSONPath matcher proposal #637

matt-simons opened this issue Feb 6, 2023 · 3 comments

Comments

@matt-simons
Copy link

Hi, I originally raised this PR kubernetes-sigs/controller-runtime#2174 which added a new matcher to a custom matcher library for Kubernetes however it was suggested that it might have wider benefits in the Gomega project itself. Just checking to see if there's any interest here.

The HaveJSONPath matcher works similarly to the HaveField matcher however it has the added benefit of JSON path expressions. This enables a simplified method of traversing complex structs and matching against specific fields.

For example, where k.Object is a helper function that polls an API and updates the contents of deployment:

id := func(element interface{}) string {
	return string(element.(appsv1.DeploymentCondition).Type)
}
Eventually(k.Object(deployment)).Should(HaveField("Status.Conditions",
	MatchElements(id, IgnoreExtras, Elements{
		"Available": MatchFields(IgnoreExtras, Fields{
			"Reason": Equal("MinimumReplicasAvailable"),
		}),
	}),
))

With HaveJSONPath:

Eventually(k.Object(deployment)).Should(HaveJSONPath(
	`{.status.conditions[?(@.type=="Available")].reason}`, Equal("MinimumReplicasAvailable")),
)

example JSON of deployment when marshalled:

{
...
    "status": {
        "conditions": [
            {
                "lastTransitionTime": "2023-01-24T15:40:30Z",
                "lastUpdateTime": "2023-01-24T15:40:39Z",
                "message": "ReplicaSet \"coredns-565d847f94\" has successfully progressed.",
                "reason": "NewReplicaSetAvailable",
                "status": "True",
                "type": "Progressing"
            },
            {
                "lastTransitionTime": "2023-02-03T13:48:06Z",
                "lastUpdateTime": "2023-02-03T13:48:06Z",
                "message": "Deployment has minimum availability.",
                "reason": "MinimumReplicasAvailable",
                "status": "True",
                "type": "Available"
            }
        ],
    }
}

HaveJSONPath also supports returning structs or slices for assertions and should work with any struct.

@JoelSpeed
Copy link

Mentioned on the other thread, a third way of writing this would be

Eventually(k.Object(deployment)).Should(HaveField("Status.Conditions",
	ContainElement(SatisfyAll(
		HaveField("Type", Equal("Available")), // Find the condition of type Available
		HaveField("Reason", Equal("MinimumReplicasAvailable")), // Check the Reason
	)),
))

which may be preferred over the previous example with MatchElements

@onsi
Copy link
Owner

onsi commented Feb 6, 2023

hey there - @JoelSpeed 's correct that there are simpler ways to pull off matchers like this without needing to use MatchgElements and MatchFields.

With that said, I love the idea of adding JSONPath support. Would this traverse raw JSON objects (i.e. is the actual value passed in to the matcher a string that the matcher interprets as JSON?) or would it operate against arbitrary Go objects?

also - is there a JSONPath dependency this would rely on - and if so, what do you have in mind? or would you be implementing a parser? (both are fine, just wanting to understand). I see that the PR you linked to above uses an implementation in the k8s go-client util directory which would be an awkward dependency to pull in vs a standalone implementation of jsonpath.

lastly - it doesn't look like there's a formal JSONPath specification - and I notice that the k8s-specific JSONPath support requires enclosing the JSONPath query in { ... } - which doesn't seem to be in any of the (proto?)-specifications or various implementations I'm seeing online. If this lands in Gomega generally vs k8s specifically I think it would need to be relatively conformant to what's out there.

@matt-simons
Copy link
Author

I like the idea that JSONPath could support both string and arbitrary Go objects.

The how and when I'm not too sure of, but I definitely agree it should follow the specification when formalised and I think ideally should use a well maintained library.

Maybe it would make most sense to revisit this once the specification is formalised?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants