Severities and blocking
info, warning and error, and which findings deny a request.
Every validation rule has a severity. An error finding blocks: the decision is deny. A
warning and an info finding do not block, so the user sees them and can still go on. The
engine reports every finding with its code, severity, blocking flag and status.
Syntax
- id: <rule id>
kind: validation
target: { entity: <Entity>, field: /<field> }
operations: [create]
assert: <expression that must be true>
severity: info | warning | error
finding: { code: <PREFIX-AREA-NNN>, message: <message key> }| Severity | Blocks the request |
|---|---|
info | never |
warning | only when it needs an acknowledgement (next lesson) |
error | yes, unless the risk is accepted (see Accept the risk) |
Example
The request has zero items, asks for express delivery and has no note. It gets one finding of each severity. Only the error blocks.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.severities, version: 1.0.0, title: "Severities" }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.min
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: gte, args: [{ var: data.quantity }, 1] }
severity: error
finding: { code: LRN-SEV-001, message: order.quantityMin }
- id: order.express.cost
kind: validation
target: { entity: Order, field: /express }
operations: [create]
assert: { op: ne, args: [{ var: data.express }, true] }
severity: warning
finding: { code: LRN-SEV-002, message: order.expressCost }
- id: order.notes.recommended
kind: validation
target: { entity: Order, field: /notes }
operations: [create]
assert: { op: exists, args: [{ var: data.notes }] }
severity: info
finding: { code: LRN-SEV-003, message: order.notesRecommended }
messages:
en:
order.quantityMin: "Order at least one item."
order.expressCost: "Express delivery costs extra."
order.notesRecommended: "A note helps the warehouse."
tests:
- name: zero items with express delivery and no note
entity: Order
operation: create
given:
data: { quantity: 0, express: true }
expect:
decision: deny
findings:
- { rule: order.quantity.min, severity: error, blocking: true, status: open }
- { rule: order.express.cost, severity: warning, blocking: false }
- { rule: order.notes.recommended, severity: info, blocking: false }
- name: warnings and infos alone do not block
entity: Order
operation: create
given:
data: { quantity: 2, express: true }
expect:
decision: allow
findings:
- { rule: order.express.cost }
- { rule: order.notes.recommended }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 0,
"express": true
}
}Result, from the engine
Decisiondeny3 findings, server channel
LRN-SEV-001errorblockingOrder at least one item./quantityLRN-SEV-002warningnot blockingExpress delivery costs extra./expressLRN-SEV-003infonot blockingA note helps the warehouse./notes
Common mistakes
- Two rules with the same finding
code. Codes identify a finding in logs and support tickets, so each rule needs its own. The ruleset does not load:FINDING_CODE_DUPLICATE.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.severities-duplicate, version: 1.0.0, title: "Two rules, one code" }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.min
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: gte, args: [{ var: data.quantity }, 1] }
severity: error
finding: { code: LRN-SEV-001, message: order.quantityMin }
- id: order.express.cost
kind: validation
target: { entity: Order, field: /express }
operations: [create]
assert: { op: ne, args: [{ var: data.express }, true] }
severity: warning
finding: { code: LRN-SEV-002, message: order.expressCost }
- id: order.notes.recommended
kind: validation
target: { entity: Order, field: /notes }
operations: [create]
assert: { op: exists, args: [{ var: data.notes }] }
severity: info
finding: { code: LRN-SEV-002, message: order.notesRecommended }
messages:
en:
order.quantityMin: "Order at least one item."
order.expressCost: "Express delivery costs extra."
order.notesRecommended: "A note helps the warehouse."
tests:
- name: zero items with express delivery and no note
entity: Order
operation: create
given:
data: { quantity: 0, express: true }
expect:
decision: deny
findings:
- { rule: order.quantity.min, severity: error, blocking: true, status: open }
- { rule: order.express.cost, severity: warning, blocking: false }
- { rule: order.notes.recommended, severity: info, blocking: false }
- name: warnings and infos alone do not block
entity: Order
operation: create
given:
data: { quantity: 2, express: true }
expect:
decision: allow
findings:
- { rule: order.express.cost }
- { rule: order.notes.recommended }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 0,
"express": true
}
}Result, from the engine
does not loadThe engine refuses the ruleset before it evaluates anything.
FINDING_CODE_DUPLICATEcode LRN-SEV-002 is also used by order.express.cost (line 30)
- Using
warningfor something that must never happen. A warning that needs no acknowledgement does not stop the request.
Exercise
Express delivery is no longer offered. Make the express rule deny the request. Write a golden test that shows an express order is denied and a standard one is allowed.
Hint
Change severity: warning to severity: error on the order.express.cost rule.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.severities, version: 1.0.0, title: "Severities" }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.min
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: gte, args: [{ var: data.quantity }, 1] }
severity: error
finding: { code: LRN-SEV-001, message: order.quantityMin }
- id: order.express.cost
kind: validation
target: { entity: Order, field: /express }
operations: [create]
assert: { op: ne, args: [{ var: data.express }, true] }
severity: error
finding: { code: LRN-SEV-002, message: order.expressCost }
- id: order.notes.recommended
kind: validation
target: { entity: Order, field: /notes }
operations: [create]
assert: { op: exists, args: [{ var: data.notes }] }
severity: info
finding: { code: LRN-SEV-003, message: order.notesRecommended }
messages:
en:
order.quantityMin: "Order at least one item."
order.expressCost: "Express delivery costs extra."
order.notesRecommended: "A note helps the warehouse."
tests:
- name: express delivery is now denied
entity: Order
operation: create
given:
data: { quantity: 2, express: true, notes: "ring twice" }
expect:
decision: deny
findings:
- { rule: order.express.cost, severity: error, blocking: true }
- name: standard delivery is allowed
entity: Order
operation: create
given:
data: { quantity: 2, express: false, notes: "ring twice" }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 2,
"express": true,
"notes": "ring twice"
}
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-SEV-002errorblockingExpress delivery costs extra./express