Rule Cascade
LearnSeverities and resolutions

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

severity, assert and finding
- 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> }
SeverityBlocks the request
infonever
warningonly when it needs an acknowledgement (next lesson)
erroryes, 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.

severities-and-blocking.ruleset.yaml
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 }
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "quantity": 0,
    "express": true
  }
}

Result, from the engine

Decisiondeny3 findings, server channel

  • LRN-SEV-001errorblockingOrder at least one item./quantity
  • LRN-SEV-002warningnot blockingExpress delivery costs extra./express
  • LRN-SEV-003infonot blockingA note helps the warehouse./notes
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

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.
severities-and-blocking-duplicate.ruleset.yaml
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 }
request.json
{
  "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_DUPLICATE code LRN-SEV-002 is also used by order.express.cost (line 30)
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.
  • Using warning for 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
severities-and-blocking.ruleset.yaml
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: [] }
request.json
{
  "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
Course overview

On this page