Rule Cascade
LearnTesting

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:

CodeRaised when
SCHEMA_INVALIDThe document does not match the JSON Schema: a missing member, a wrong type, a value outside an enum
SCHEMA_REF_UNRESOLVEDAn entity's schema $ref cannot be resolved
ENTITY_UNKNOWNA rule targets an entity that is not declared
TYPE_UNKNOWNA rule targets, or a field is bound to, a type that is not declared
PATH_UNKNOWNA data.* or original.* path, a field or a pointer is not in the entity schema
PARAM_UNDECLAREDparams.<name> is not declared
SCOPE_INVALIDA root is used where it is not in scope, such as item outside forEach
FUNCTION_UNKNOWNA call names a function that is not declared
FUNCTION_ARITYA call passes the wrong number of arguments
FUNCTION_RECURSIVEA function calls itself, directly or indirectly
OPERATOR_UNDECLAREDAn x-* operator is used but not declared under operators
ORIGINAL_ON_CREATEA rule that applies to create reads original.*
MESSAGE_MISSINGA finding's message key is not in the default locale
MESSAGE_ARG_MISSINGA message placeholder has no matching finding.args entry
FINDING_CODE_DUPLICATETwo rules use the same finding code
PATTERN_NOT_PORTABLEA matches pattern is not a literal, or is outside the portable subset
EXPRESSION_TOO_DEEPAn 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.

load-checks.ruleset.yaml
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 }
request.json
{
  "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)
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

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:

load-checks-ref.ruleset.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 }
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "quantity": 11
  }
}

Result, from the engine

does not loadThe engine refuses the ruleset before it evaluates anything.

  • SCHEMA_REF_UNRESOLVED entity Order: ./orders.openapi.yaml#/components/schemas/Order not found (line 8)
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

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
load-checks-ref.ruleset.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: "./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 }
request.json
{
  "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
load-checks.ruleset.yaml
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 }
request.json
{
  "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
Course overview

On this page