Skip to content

Validate JSON document during auto-rules creation #539

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

Merged

Conversation

jan-law
Copy link
Contributor

@jan-law jan-law commented Jun 24, 2021

Fixes #530

@andrewazores I found a JsonDeserializer that can reuse requireNonNegative() and I have some questions about how to implement it.

private final Gson gson needs to be modified in order to register the Type Adapter for IntegerDeserializer. Why is gson final?

IntegerDeserializer gets passed JSON values without their keys, which means I'm not sure how to pass the correct Attribute to requireNonNegative().

IntegerDeserializer only works if our numeric values are of type Integer, not int. How will this affect performance?

Here's the output as of now:

$ vi testRule.json

{
   "name": "test",
   "description": "AutoRulesIT automated rule",
   "eventSpecifier": "template=Continuous,type=TARGET",
   "targetAlias": "es.andrewazor.demo.Main",
   "archivalPeriodSeconds": 60,
   "preservedArchives": -10,
   "maxAgeSeconds": 60
}

$ curl -v --insecure -H "Content-Type: application/json"  -d @testRule.json https://0.0.0.0:8181/api/v2/rules/
...
< HTTP/1.1 400 Bad Request
< content-type: application/json
< content-length: 131
< 
* Connection #0 to host 0.0.0.0 left intact
{"meta":{"type":"text/plain","status":"Bad Request"},"data":{"reason":"\"archivalPeriodSeconds\" cannot be negative, was \"-10\""}}

Some of the other approaches I've thought of include parsing params.getBody() before calling gson.fromJson(...) or using a Rule.Builder similar to the JSON form pathway. I'd like to avoid parsing the JSON more than once though, and am not sure how to efficiently map key/value pairs from the params.getBody() string to the Rule.Builder.

@andrewazores
Copy link
Member

andrewazores commented Jun 24, 2021

private final Gson gson needs to be modified in order to register the Type Adapter for IntegerDeserializer. Why is gson final?

The gson instance, and generally all other constructor-assigned fields that are provided by dependency injection, is final because the field reference should never need to be overwritten. Changing the field reference implies that the injected dependency is no longer being used, which violates the purpose of injecting the common dependency. In the case of the Gson instance, it's because the MainModule provides a single instance so that we have uniform behaviour of (de)serialization across the application.

IntegerDeserializer gets passed JSON values without their keys, which means I'm not sure how to pass the correct Attribute to requireNonNegative().

This makes me think this may not be the correct deserializer to use, then. If I'm understanding correctly, this is what Gson will use when deserializing something in JSON that looks like an integer value, so that you could instead map that to a different type than the Java Integer.

IntegerDeserializer only works if our numeric values are of type Integer, not int. How will this affect performance?

It's probably not of much concern.

I'd like to avoid parsing the JSON more than once though, and am not sure how to efficiently map key/value pairs from the params.getBody() string to the Rule.Builder.

Another possible path along this line of thought is to extract the validation that currently occurs in the Rule constructor (which is called by the Builder.build()) into a method like Rule.validate(). The Rule constructor can then call validate() at the end, after it has assigned all of its own fields. In the JSON parsing pathway of the handler, the handler can call rule.validate() on the Rule instance that is created by Gson.

However, the cleanest approach may be to use a deserializer/adapter, so that Gson itself knows how to deal with invalid Rule JSON definitions, and we don't have to ensure we call rule.validate() everywhere one may be deserialized. Our existing GsonJmxServiceUrlAdapter could be a good reference for this.

@andrewazores
Copy link
Member

Another possible path along this line of thought is to extract the validation that currently occurs in the Rule constructor (which is called by the Builder.build()) into a method like Rule.validate(). The Rule constructor can then call validate() at the end, after it has assigned all of its own fields. In the JSON parsing pathway of the handler, the handler can call rule.validate() on the Rule instance that is created by Gson.

However, the cleanest approach may be to use a deserializer/adapter, so that Gson itself knows how to deal with invalid Rule JSON definitions, and we don't have to ensure we call rule.validate() everywhere one may be deserialized. Our existing GsonJmxServiceUrlAdapter could be a good reference for this.

You know, now that I've said this, maybe the correct approach is to combine them. There should be a Rule#validate() method which the Rule constructor can call to ensure its own field validity, and this can also be called by the Gson adapter after deserializing the Rule JSON.

@andrewazores
Copy link
Member

This is probably for later enhancements in this same area:

https://stackoverflow.com/questions/21626690/gson-optional-and-required-fields

We could use an approach like this to create annotations and register adapters/deserializers to perform validations, like ensuring deserialized reference-type fields are non-null, or numeric types are positive, etc.

@jan-law jan-law force-pushed the 530-validate-numeric-values-in-json branch from 73d8087 to 18356c4 Compare June 24, 2021 19:11
@jan-law
Copy link
Contributor Author

jan-law commented Jun 25, 2021

gson will successfully deserialize -0 as 0. Should we leave this as is?

@andrewazores
Copy link
Member

That's fine.

$ jshell
|  Welcome to JShell -- Version 11.0.11
|  For an introduction type: /help intro

jshell> -0 == 0
$1 ==> true

@jan-law jan-law force-pushed the 530-validate-numeric-values-in-json branch 2 times, most recently from 6c0d95a to 2645c40 Compare June 25, 2021 19:43
@jan-law jan-law force-pushed the 530-validate-numeric-values-in-json branch from 2645c40 to 86581a1 Compare June 25, 2021 20:26
@jan-law jan-law force-pushed the 530-validate-numeric-values-in-json branch from 488778b to fde579b Compare June 29, 2021 17:24
@jan-law jan-law marked this pull request as ready for review June 29, 2021 17:34
andrewazores
andrewazores previously approved these changes Jun 30, 2021
@jan-law
Copy link
Contributor Author

jan-law commented Jun 30, 2021

I'm done making changes. PR is ready to merge

@andrewazores andrewazores merged commit 8245eb7 into cryostatio:main Jun 30, 2021
@jan-law jan-law deleted the 530-validate-numeric-values-in-json branch June 30, 2021 17:47
aali309 pushed a commit to aali309/cryostat-legacy that referenced this pull request Jul 22, 2024
…6.0 to 4.8.6.1 (cryostatio#539)

build(deps): bump com.github.spotbugs:spotbugs-maven-plugin

Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.8.6.0 to 4.8.6.1.
- [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases)
- [Commits](spotbugs/spotbugs-maven-plugin@spotbugs-maven-plugin-4.8.6.0...spotbugs-maven-plugin-4.8.6.1)

---
updated-dependencies:
- dependency-name: com.github.spotbugs:spotbugs-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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

Successfully merging this pull request may close these issues.

Auto-Rules JSON creation does not validate numeric values
2 participants