2 Usage - Reference Documentation
Authors: Aaron Brown
Version: 1.0.0
2 Usage
This section will describe general usage of Sanity Checkers, as well as usage of the packaged extensions that come with the plugin.2.1 General Usage
General usage of any Sanity Checker is to run the various checks, and then determining if there were any failures. The scope of how to handle failures if they are detected falls outside of this guide, as that is dependant on the application or circumstances of the failure.Running Sanity Checks
TheThe basic methodology for running sanity checks is roughly the following:BasicSanityChecker
is used in these examples, as theSanityChecker
is too limited to adiquately demonstrate usage.
- Instantiate a sanity checker.
- Provide the
entity
to check. - Run the checks (runChecks).
- Detect Failures.
- React to results.
import me.sudofu.sanitycheck.BasicSanityChecker// ...BasicSanityChecker checker = new BasicSanityChecker()checker.runChecks { check('a', a, 'parameter').isString() check('b', b) isMap() isNotEmpty() check('b.age', b.age).isInteger() }if (checker.hasFailures()) { // Respond to failures. }// ...
check
, the classification 'parameter' is
used. By default, the classification is hard-coded and configured as 'entity';
it is possible to configure the default value through configuring the
default classification
. Additionaly, this configuration can be overridden by
parameter as shown here.ForBasicSanityChecker
andStringCoerciveSanityChecker
, and any other extensions which are written appropriately, invoking a sanity check prior to calling check for the first time will result in anIllegalStateException
.This does not need to be put in atry / catch
clause, as this is to prevent developer folly.
While the closure-style is recommended, more verbose calling structures are possible, but will not be discussed in this guide.
Allow Pass on null
Sanity checkers outside the ones packaged in this plugin may not be written to implement this feature. If writing such a sanity checker, please indicate such.All references to sanity checkers is only confidently referring to sanity checkers provided by the plugin.
isNotNull inherently does not follow the behavior outlined
in this section. It will always result in a failure if the entity being
checked is null
.
Generally speaking, null
changes the behavior of sanity checks. All sanity
checkers are designed to accommodate two methodologies:
- Check if the entity is
null
, and if so report failure. - Check if the entity is
null
, and if so allow it to pass.
null
. This default
configuration can be changed via allowPassOnNull
.The next level of granularity is in the SanityChecker
constructor. This
will set the default behavior for that sanity checker.The next level of granularity is through use of either directly modifying
allowPassOnNull
, or invoking allowPassOnNull()
/ disallowPassOnNull()
.
This will alter the behavior over the course of a sanity checker's life.The final level of granularity is via the allowPassOnNull parameter at
every sanity check. This is the finest granularity and only impacts the
particular sanity check performed.These levels of granularity provide extremely high flexibility for this
behavior. Of course, it is best to develop with the mindset of using the
highest level necessary, to avoid confusing the behavior.An example use-case for using both methodologies in one sanity checker is to
check optional parameters. In many cases, optional parameters are allowed to
be null
, and therefore it is undesirable to let that interfere with sanity
checks if they are not present, but abide by sanity checks if they are.Refer to the following example to see this implemention using Groovy Named
Parameters. The method first checks the required parameters, assuming that the
default behavior is set to disallow null
to pass, and then allows null
to
pass when checking the optional parameters.public void myMethod(Map options, String a, Date birthday, int… luckyNumbers) { BasicSanityChecker checker = new BasicSanityChecker().runChecks { check('a', a).isNotEmpty() check('birthday', myBirthday).isNotNull() luckyNumbers.eachWithIndex { index, number -> check("luckyNumber[${index}]", number).isNotNull() } allowPassOnNull() options.with { check('favoriteColor', myFavoriteColor) isString() isNotEmpty() check('height', myHeight) isDouble() check('myEyesAreBlue', myEyesAreBlue) isBoolean() } } if (checker.hasFailures) { // Handle the result. } }
2.2 BasicSanityChecker
The BasicSanityChecker is a very strict sanity checker. It is used when only strict datatyping needs to be enforced. It is arguably less useful if an application is strictly typed, however it is provided for completeness and for giving mnemonics to certain sanity checks that can otherwise be implemented manually.However, due to the nature of some extensions, the simplistic nature of theBasicSanityChecker
gives it some sanity checks that may not be feasible with
other extensions.Much like the root SanityChecker
this checker is likely best used as a more
fleshed-out basis for extension.Sample:public void myMethod(Map options, String a, Date birthday, int… luckyNumbers) { BasicSanityChecker checker = new BasicSanityChecker().runChecks { check('a', a).isNotEmpty() check('birthday', myBirthday).isNotNull() luckyNumbers.eachWithIndex { index, number -> check("luckyNumber[${index}]", number).isNotNull() } allowPassOnNull() options.with { check('favoriteColor', myFavoriteColor) isString() isNotEmpty() check('height', myHeight) isDouble() check('myEyesAreBlue', myEyesAreBlue) isBoolean() } } if (checker.hasFailures) { // Handle the result. } }
2.3 StringCoerciveSanityChecker
The StringCoerciveSanityChecker is designed to perform sanity checks, with the added serendipity of coercingString
entities where possible.This sanity checker is intended for general use, especially where inputs are
not strictly typed, and may be normalized as String
. For instance, JSON in
a JAXRS Resource, or with Groovy's Named Parameters.The StringCoerciveSanityChecker handles the entity
along with determining
pass / fail of the check:
- Run the sanity check.
- If the entity is a
String
, and theString
is coercible to the type the sanity check is looking for, return the entity coerced to that type. - Otherwise, return the entity.
import me.sudofu.sanitycheck.SanityChecker import me.sudofu.sanitycheck.BasicSanityChecker import me.sudofu.sanitycheck.StringCoerciveSanityChecker as SCSanityCheckerpublic void myMethod(Map options, String a, Date birthday, int… luckyNumbers) { SanityChecker checker = new SCSanityChecker().runChecks { check('a', a).isNotEmpty() check('birthday', myBirthday).isNotNull() luckyNumbers.eachWithIndex { index, number -> luckyNumbers[index] = check("luckyNumber[${index}]", number).isNotNull() } allowPassOnNull() options.with { check('favoriteColor', myFavoriteColor) isString() isNotEmpty() myHeight = check('height', myHeight) } } if (checker.hasFailures) { // Handle the result. } checker = new BasicSanityChecker() .check('myEyesAreBlue', options.myEyesAreBlue) .isBoolean() if (checker.hasFailures) { // Handle the result. } }
StringCoerciveSanityChecker
. Also note that isBoolean is
not within StringCoerciveSanityChecker
, so it has to be checked separately.
The justification of this is because a String
is not very intuitively
coercible into Boolean
, since both "false"
and "puppies"
would both
resolve to false.The behavior ofStringCoerciveSanityChecker
is subject to Groovy's string-coersion algorithms. Therefore, a change in Groovy's string-coersion algorithms will change the behavior of this sanity checker.For example,"15D"
will successfully pass and be coerced from isDouble, but"15I"
will not pass nor be coerced from isInteger.