Params and override policies
Typed constants that a child ruleset may change only as far as the parent allows.
A param is a typed constant a rule reads as params.<name>. The parent declares it with a type
and a default. A child changes it under overrides.params, as far as the param's
overridePolicy allows: locked (never), tighten-only (only in its tightenDirection, lower
or higher) or open (any value).
Syntax
params:
maxQuantity:
type: integer # string, number, integer, boolean, stringList, numberList
default: 10
description: Most items in one order.
overridePolicy: tighten-only
tightenDirection: loweroverrides:
params:
maxQuantity: 5Example
store.ruleset.yaml declares four params, one of each policy. The kiosk lowers maxQuantity to 5
and turns gift wrapping off. A request for 7 items is denied, and the message shows the kiosk's
limit.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.kiosk, version: 1.0.0, title: "Kiosk rules" }
scope:
- { level: organization, id: learn }
- { level: project, id: kiosk }
extends:
- { ruleset: learn.store, version: "^1.0.0" }
overrides:
params:
maxQuantity: 5
giftWrapAllowed: false
rules: []
tests:
- name: the kiosk allows only five items
entity: Order
operation: create
given:
data: { quantity: 7, total: 30, currency: "EUR" }
expect:
decision: deny
findings:
- { rule: store.quantity.max, message: "You can order at most 5 items." }
- name: the kiosk does not gift wrap
entity: Order
operation: create
given:
data: { quantity: 2, total: 30, currency: "EUR", giftWrap: true }
expect:
decision: deny
findings:
- { rule: store.gift-wrap }
- name: five items are fine
entity: Order
operation: create
given:
data: { quantity: 5, total: 30, currency: "EUR" }
expect: { decision: allow, findings: [] }ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.store, version: 1.0.0, title: "Store rules" }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
params:
maxQuantity:
type: integer
default: 10
description: Most items in one order.
overridePolicy: tighten-only
tightenDirection: lower
minTotal:
type: number
default: 5
description: Smallest order total.
overridePolicy: tighten-only
tightenDirection: higher
currency:
type: string
default: EUR
overridePolicy: locked
giftWrapAllowed:
type: boolean
default: true
overridePolicy: open
rules:
- id: store.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, { var: params.maxQuantity }] }
severity: error
finding: { code: LRN-PAR-001, message: store.quantityMax, args: { max: { var: params.maxQuantity } } }
- id: store.total.min
kind: validation
target: { entity: Order, field: /total }
operations: [create]
assert: { op: gte, args: [{ var: data.total }, { var: params.minTotal }] }
severity: error
finding: { code: LRN-PAR-002, message: store.totalMin, args: { min: { var: params.minTotal } } }
- id: store.currency
kind: validation
target: { entity: Order, field: /currency }
operations: [create]
assert: { op: eq, args: [{ var: data.currency }, { var: params.currency }] }
severity: error
finding: { code: LRN-PAR-003, message: store.currency, args: { currency: { var: params.currency } } }
- id: store.gift-wrap
kind: validation
target: { entity: Order, field: /giftWrap }
operations: [create]
assert: { op: or, args: [{ op: ne, args: [{ var: data.giftWrap }, true] }, { var: params.giftWrapAllowed }] }
severity: error
finding: { code: LRN-PAR-004, message: store.giftWrap }
messages:
en:
store.quantityMax: "You can order at most {max} items."
store.totalMin: "The smallest order is {min}."
store.currency: "This store sells in {currency}."
store.giftWrap: "Gift wrapping is not available here."{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 7,
"total": 30,
"currency": "EUR"
}
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-PAR-001errorblockingYou can order at most 5 items./quantity
Common mistakes
- Moving a
tighten-onlyparam the wrong way. The kiosk below raisesmaxQuantityto 20, so it does not load:PARAM_LOOSENED. - Changing a
lockedparam (PARAM_LOCKED), overriding a param nobody declared (PARAM_UNKNOWN), declaring an inherited param again (PARAM_REDEFINED), a value of the wrong type (PARAM_TYPE_MISMATCH), or readingparams.xthat is not declared (PARAM_UNDECLARED).
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.kiosk, version: 1.0.0, title: "Kiosk rules" }
scope:
- { level: organization, id: learn }
- { level: project, id: kiosk }
extends:
- { ruleset: learn.store, version: "^1.0.0" }
overrides:
params:
maxQuantity: 20
rules: []
tests:
- name: the kiosk allows only twenty items
entity: Order
operation: create
given:
data: { quantity: 25, total: 30, currency: "EUR" }
expect:
decision: deny
findings:
- { rule: store.quantity.max, message: "You can order at most 20 items." }
- name: twenty items are fine
entity: Order
operation: create
given:
data: { quantity: 20, total: 30, currency: "EUR" }
expect: { decision: allow, findings: [] }ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.store, version: 1.0.0, title: "Store rules" }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
params:
maxQuantity:
type: integer
default: 10
description: Most items in one order.
overridePolicy: tighten-only
tightenDirection: lower
minTotal:
type: number
default: 5
description: Smallest order total.
overridePolicy: tighten-only
tightenDirection: higher
currency:
type: string
default: EUR
overridePolicy: locked
giftWrapAllowed:
type: boolean
default: true
overridePolicy: open
rules:
- id: store.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, { var: params.maxQuantity }] }
severity: error
finding: { code: LRN-PAR-001, message: store.quantityMax, args: { max: { var: params.maxQuantity } } }
- id: store.total.min
kind: validation
target: { entity: Order, field: /total }
operations: [create]
assert: { op: gte, args: [{ var: data.total }, { var: params.minTotal }] }
severity: error
finding: { code: LRN-PAR-002, message: store.totalMin, args: { min: { var: params.minTotal } } }
- id: store.currency
kind: validation
target: { entity: Order, field: /currency }
operations: [create]
assert: { op: eq, args: [{ var: data.currency }, { var: params.currency }] }
severity: error
finding: { code: LRN-PAR-003, message: store.currency, args: { currency: { var: params.currency } } }
- id: store.gift-wrap
kind: validation
target: { entity: Order, field: /giftWrap }
operations: [create]
assert: { op: or, args: [{ op: ne, args: [{ var: data.giftWrap }, true] }, { var: params.giftWrapAllowed }] }
severity: error
finding: { code: LRN-PAR-004, message: store.giftWrap }
messages:
en:
store.quantityMax: "You can order at most {max} items."
store.totalMin: "The smallest order is {min}."
store.currency: "This store sells in {currency}."
store.giftWrap: "Gift wrapping is not available here."{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 25,
"total": 30,
"currency": "EUR"
}
}Result, from the engine
does not loadThe engine refuses the ruleset before it evaluates anything.
PARAM_LOOSENEDlearn.kiosk: param maxQuantity may only move lower (line 11)
Exercise
The kiosk does not take orders below 20. Override minTotal and add a test where a total of 15
is denied with the kiosk's minimum in the message.
Hint
minTotal is tighten-only with tightenDirection: higher, so a bigger value is allowed. Add minTotal: 20 under overrides.params.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.kiosk, version: 1.0.0, title: "Kiosk rules" }
scope:
- { level: organization, id: learn }
- { level: project, id: kiosk }
extends:
- { ruleset: learn.store, version: "^1.0.0" }
overrides:
params:
maxQuantity: 5
giftWrapAllowed: false
minTotal: 20
rules: []
tests:
- name: the kiosk allows only five items
entity: Order
operation: create
given:
data: { quantity: 7, total: 25, currency: "EUR" }
expect:
decision: deny
findings:
- { rule: store.quantity.max, message: "You can order at most 5 items." }
- name: the kiosk does not gift wrap
entity: Order
operation: create
given:
data: { quantity: 2, total: 25, currency: "EUR", giftWrap: true }
expect:
decision: deny
findings:
- { rule: store.gift-wrap }
- name: five items are fine
entity: Order
operation: create
given:
data: { quantity: 5, total: 25, currency: "EUR" }
expect: { decision: allow, findings: [] }
- name: the kiosk minimum is 20
entity: Order
operation: create
given:
data: { quantity: 1, total: 15, currency: "EUR" }
expect:
decision: deny
findings:
- { rule: store.total.min, message: "The smallest order is 20." }ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.store, version: 1.0.0, title: "Store rules" }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
params:
maxQuantity:
type: integer
default: 10
description: Most items in one order.
overridePolicy: tighten-only
tightenDirection: lower
minTotal:
type: number
default: 5
description: Smallest order total.
overridePolicy: tighten-only
tightenDirection: higher
currency:
type: string
default: EUR
overridePolicy: locked
giftWrapAllowed:
type: boolean
default: true
overridePolicy: open
rules:
- id: store.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, { var: params.maxQuantity }] }
severity: error
finding: { code: LRN-PAR-001, message: store.quantityMax, args: { max: { var: params.maxQuantity } } }
- id: store.total.min
kind: validation
target: { entity: Order, field: /total }
operations: [create]
assert: { op: gte, args: [{ var: data.total }, { var: params.minTotal }] }
severity: error
finding: { code: LRN-PAR-002, message: store.totalMin, args: { min: { var: params.minTotal } } }
- id: store.currency
kind: validation
target: { entity: Order, field: /currency }
operations: [create]
assert: { op: eq, args: [{ var: data.currency }, { var: params.currency }] }
severity: error
finding: { code: LRN-PAR-003, message: store.currency, args: { currency: { var: params.currency } } }
- id: store.gift-wrap
kind: validation
target: { entity: Order, field: /giftWrap }
operations: [create]
assert: { op: or, args: [{ op: ne, args: [{ var: data.giftWrap }, true] }, { var: params.giftWrapAllowed }] }
severity: error
finding: { code: LRN-PAR-004, message: store.giftWrap }
messages:
en:
store.quantityMax: "You can order at most {max} items."
store.totalMin: "The smallest order is {min}."
store.currency: "This store sells in {currency}."
store.giftWrap: "Gift wrapping is not available here."{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 7,
"total": 25,
"currency": "EUR"
}
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-PAR-001errorblockingYou can order at most 5 items./quantity