JSON Schema Rule Validation
robnewton opened this issue · 3 comments
@CacheControl Nice work on the engine.
We are implementing our business rule editor (JSON editor) in a React app that then stores them into a DB using GraphQL. In order to ensure the JSON being typed into our rule editor is structurally in line with what JRE expects, I need to compare it to a schema. I looked around and didn't see one anywhere so I created one and have provided it below.
Would anyone be able to help test the schema with some of your own rules to make sure it is truly covering all possible scenarios? I followed the documentation but suspect I may not have caught everything.
Testing is pretty simple using this tool I found online: https://www.jsonschemavalidator.net/
Just copy paste the below schema into the left side of the screen and your business rule JSON on the right. It will give you any validation errors if the rule is not valid according to this schema. If you get any errors that you know are not accurate, please let me know so I can fix the schema. If possible, please provide the business rule JSON.
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://github.com/CacheControl/json-rules-engine/rule-schema.json",
"type": "object",
"title": "Business Rule",
"description": "Rules contain a set of conditions and a single event. When the engine is run, each rule condition is evaluated. If the results are truthy, the rule's event is triggered.",
"required": [
"conditions",
"event"
],
"properties": {
"conditions": {
"$ref": "#/definitions/conditions"
},
"event": {
"$id": "#/properties/event",
"type": "object",
"title": "The Event Schema",
"description": "Sets the on('success') and on('failure') event argument emitted whenever the rule passes. Event objects must have a type property, and an optional params property.",
"default": {},
"examples": [
{
"type": "fouledOut",
"params": {
"message": "Player has fouled out!"
}
}
],
"required": [
"type"
],
"properties": {
"type": {
"$id": "#/properties/event/properties/type",
"type": "string",
"title": "Event Type",
"description": "A string representing the type of event this describes.",
"default": "",
"examples": [
"fouledOut"
]
},
"params": {
"$id": "#/properties/event/properties/params",
"type": "object",
"title": "Event Params",
"description": "Optional helper params to make available to the event processor.",
"default": {},
"examples": [
{
"customProperty": "customValue"
}
]
}
}
},
"name": {
"$id": "#/properties/name",
"anyOf": [
{
"type": "string"
},
{
"type": "object"
},
{
"type": "array"
},
{
"type": "number"
}
],
"title": "The Name Schema",
"description": "A way of naming your rules, allowing them to be easily identifiable in Rule Results. This is usually of type String, but could also be Object, Array, or Number. Note that the name need not be unique, and that it has no impact on execution of the rule.",
"default": {},
"examples": [
"My Rule Name"
]
},
"priority": {
"$id": "#/properties/priority",
"anyOf": [
{
"type": "integer",
"minimum": 1
}
],
"title": "Priority",
"description": "Dictates when rule should be run, relative to other rules. Higher priority rules are run before lower priority rules. Rules with the same priority are run in parallel. Priority must be a positive, non-zero integer.",
"default": 1,
"examples": [
1
]
}
},
"definitions": {
"conditions": {
"type": "object",
"title": "Conditions",
"description": "Rule conditions are a combination of facts, operators, and values that determine whether the rule is a success or a failure. The simplest form of a condition consists of a fact, an operator, and a value. When the engine runs, the operator is used to compare the fact against the value. Each rule's conditions must have either an all or an any operator at its root, containing an array of conditions. The all operator specifies that all conditions contained within must be truthy for the rule to be considered a success. The any operator only requires one condition to be truthy for the rule to succeed.",
"default": {},
"examples": [
{
"all": [
{
"value": true,
"fact": "displayMessage",
"operator": "equal"
}
]
}
],
"oneOf": [
{
"required": [
"any"
]
},
{
"required": [
"all"
]
}
],
"properties": {
"any": {
"$ref": "#/definitions/conditionArray"
},
"all": {
"$ref": "#/definitions/conditionArray"
}
}
},
"conditionArray": {
"type": "array",
"title": "Condition Array",
"description": "An array of conditions with a possible recursive inclusion of another condition array.",
"default": [],
"items": {
"anyOf": [
{
"$ref": "#/definitions/conditions"
},
{
"$ref": "#/definitions/condition"
}
]
}
},
"condition": {
"type": "object",
"title": "Condition",
"description": "Rule conditions are a combination of facts, operators, and values that determine whether the rule is a success or a failure. The simplest form of a condition consists of a fact, an operator, and a value. When the engine runs, the operator is used to compare the fact against the value. Sometimes facts require additional input to perform calculations. For this, the params property is passed as an argument to the fact handler. params essentially functions as fact arguments, enabling fact handlers to be more generic and reusable.",
"default": {
"fact": "my-fact",
"operator": "lessThanInclusive",
"value": 1
},
"examples": [
{
"fact": "gameDuration",
"operator": "equal",
"value": 40.0
},
{
"value": 5.0,
"fact": "personalFoulCount",
"operator": "greaterThanInclusive"
},
{
"fact": "product-price",
"operator": "greaterThan",
"path": "$.price",
"value": 100.0,
"params": {
"productId": "widget"
}
}
],
"required": [
"fact",
"operator",
"value"
],
"properties": {
"fact": {
"type": "string",
"title": "Fact",
"description": "Facts are methods or constants registered with the engine prior to runtime and referenced within rule conditions. Each fact method should be a pure function that may return a either computed value, or promise that resolves to a computed value. As rule conditions are evaluated during runtime, they retrieve fact values dynamically and use the condition operator to compare the fact result with the condition value.",
"default": "",
"examples": [
"gameDuration"
]
},
"operator": {
"type": "string",
"anyOf": [
{
"const": "equal",
"title": "fact must equal value"
},
{
"const": "notEqual",
"title": "fact must not equal value"
},
{
"const": "these",
"title": "erators use strict equality (===) and inequality (!==)"
},
{
"const": "lessThan",
"title": "fact must be less than value"
},
{
"const": "lessThanInclusiv",
"title": "fact must be less than or equal to value"
},
{
"const": "greaterThan",
"title": "fact must be greater than value"
},
{
"const": "greaterThanInclusiv",
"title": "fact must be greater than or equal to value"
},
{
"const": "in",
"title": "fact must be included in value (an array)"
},
{
"const": "notIn",
"title": "fact must not be included in value (an array)"
},
{
"const": "contains",
"title": "fact (an array) must include value"
},
{
"const": "doesNotContain",
"title": "fact (an array) must not include value"
},
{
"type": "string"
}
],
"title": "Operator",
"description": "The operator compares the value returned by the fact to what is stored in the value property. If the result is truthy, the condition passes.",
"default": "",
"examples": [
"equal"
]
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "object"
},
{
"type": "array"
},
{
"type": "number"
},
{
"type": "boolean"
}
],
"title": "Value",
"description": "The value the fact should be compared to.",
"default": 0,
"examples": [
40
]
},
"params": {
"type": "object",
"title": "Event Params",
"description": "Optional helper params to make available to the event processor.",
"default": {},
"examples": [
{
"customProperty": "customValue"
}
]
},
"path": {
"type": "string",
"title": "Path",
"description": "For more complex data structures, writing a separate fact handler for each object property quickly becomes verbose and unwieldy. To address this, a path property may be provided to traverse fact data using json-path syntax. Json-path support is provided by jsonpath-plus",
"default": "",
"examples": [
"$.price"
]
}
}
}
}
}
Yes my rules are valid with this schema !
FYI for anyone coming across this we are using it to validate rules defined in a React app in conjunction with https://www.npmjs.com/package/react-json-editor-ajrm
It’s working fine
👍 looks good to me! nice job on the example values; really helpful.
I did notice there's type-o in the operators list that's not one of the official ones (could be custom):
{
"const": "these",
"title": "erators use strict equality (===) and inequality (!==)"
},