Load-time checks
The compiler refuses a broken ruleset before it can reach users. Here is everything it checks.
Loading a ruleset runs a fixed list of checks. A ruleset that fails one never loads, and a runtime never serves it. So a typo in a field name is a failed build, not a wrong decision in production.
First the document is validated against the JSON Schema (SCHEMA_INVALID). Then inheritance is
resolved and the static checks run. Every problem the static checks find is reported together.
Syntax
Every check has a stable problem code:
| Code | Raised when |
|---|---|
SCHEMA_INVALID | The document does not match the JSON Schema: a missing member, a wrong type, a value outside an enum |
SCHEMA_REF_UNRESOLVED | An entity's schema $ref cannot be resolved |
ENTITY_UNKNOWN | A rule targets an entity that is not declared |
TYPE_UNKNOWN | A rule targets, or a field is bound to, a type that is not declared |
PATH_UNKNOWN | A data.* or original.* path, a field or a pointer is not in the entity schema |
PARAM_UNDECLARED | params.<name> is not declared |
SCOPE_INVALID | A root is used where it is not in scope, such as item outside forEach |
FUNCTION_UNKNOWN | A call names a function that is not declared |
FUNCTION_ARITY | A call passes the wrong number of arguments |
FUNCTION_RECURSIVE | A function calls itself, directly or indirectly |
OPERATOR_UNDECLARED | An x-* operator is used but not declared under operators |
ORIGINAL_ON_CREATE | A rule that applies to create reads original.* |
MESSAGE_MISSING | A finding's message key is not in the default locale |
MESSAGE_ARG_MISSING | A message placeholder has no matching finding.args entry |
FINDING_CODE_DUPLICATE | Two rules use the same finding code |
PATTERN_NOT_PORTABLE | A matches pattern is not a literal, or is outside the portable subset |
EXPRESSION_TOO_DEEP | An expression is nested more than 128 deep |
Inheritance has its own codes (EXTENDS_*, PARAM_*, RULE_*, FUNCTION_REDEFINED,
FIELD_TYPE_REBOUND, SCOPE_NOT_NARROWER). The lessons on levels and
inheritance show them.
Example
fatal is not a severity. The schema allows only info, warning and error, so the ruleset
fails with SCHEMA_INVALID.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.load-checks, version: 1.0.0, title: Load checks }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: fatal
finding: { code: LRN-CHK-001, message: order.tooMany }
messages:
en:
order.tooMany: "You can order at most 10 items."
tests:
- name: eleven items are denied
entity: Order
operation: create
given:
data: { quantity: 11 }
expect:
decision: deny
findings:
- { rule: order.quantity.max }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
}
}Result, from the engine
does not loadThe engine refuses the ruleset before it evaluates anything.
SCHEMA_INVALID/rules/0/severity: must be equal to one of the allowed values (line 15)
Common mistakes
An entity names its schema document by file name. If the file is not there, the ruleset fails with
SCHEMA_REF_UNRESOLVED. Here the ruleset points at orders.openapi.yaml, but the schema of this
course is learn.openapi.yaml:
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.load-checks-ref, version: 1.0.0, title: Load checks }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./orders.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: error
finding: { code: LRN-CHK-001, message: order.tooMany }
messages:
en:
order.tooMany: "You can order at most 10 items."
tests:
- name: eleven items are denied
entity: Order
operation: create
given:
data: { quantity: 11 }
expect:
decision: deny
findings:
- { rule: order.quantity.max }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
}
}Result, from the engine
does not loadThe engine refuses the ruleset before it evaluates anything.
SCHEMA_REF_UNRESOLVEDentity Order: ./orders.openapi.yaml#/components/schemas/Order not found (line 8)
Exercise
Fix the schema reference so that the ruleset loads and eleven items are denied.
Hint
Change the file name in the $ref. Keep the #/components/schemas/Order part.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.load-checks-ref, version: 1.0.0, title: Load checks }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: error
finding: { code: LRN-CHK-001, message: order.tooMany }
messages:
en:
order.tooMany: "You can order at most 10 items."
tests:
- name: eleven items are denied
entity: Order
operation: create
given:
data: { quantity: 11 }
expect:
decision: deny
findings:
- { rule: order.quantity.max }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
}
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-CHK-001errorblockingYou can order at most 10 items./quantity
Exercise
Fix the severity of the first ruleset so that it loads and denies eleven items.
Hint
Pick one of the three severities the schema allows.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.load-checks, version: 1.0.0, title: Load checks }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: error
finding: { code: LRN-CHK-001, message: order.tooMany }
messages:
en:
order.tooMany: "You can order at most 10 items."
tests:
- name: eleven items are denied
entity: Order
operation: create
given:
data: { quantity: 11 }
expect:
decision: deny
findings:
- { rule: order.quantity.max }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
}
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-CHK-001errorblockingYou can order at most 10 items./quantity