LearnOperators
Comparison operators
eq, ne, lt, lte, gt, gte and between compare two values and return true or false.
An operator call is an object with op and args. Comparison operators take values and return
true or false. eq and ne compare any two values. lt, lte, gt, gte and between
compare numbers only.
Syntax
{ op: <operator>, args: [<argument>, <argument>] }| Operator | Arguments | Result |
|---|---|---|
eq, ne | any, any | Equal, or not equal. Deep: lists element by element, objects key by key |
lt, lte, gt, gte | number, number | Less than, at most, greater than, at least |
between | number, low, high | low <= number <= high |
Example
Seven rules check one order. The request orders zero items, in dollars, in a 40 kg parcel.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.comparison, version: 1.0.0, title: Comparison operators }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.positive
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: gt, args: [{ var: data.quantity }, 0] }
severity: error
finding: { code: CMP-001, message: quantity.positive }
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 100] }
severity: error
finding: { code: CMP-002, message: quantity.max }
- id: order.price.min
kind: validation
target: { entity: Order, field: /price }
operations: [create]
assert: { op: gte, args: [{ var: data.price }, 1] }
severity: error
finding: { code: CMP-003, message: price.min }
- id: order.discount.below-price
kind: validation
target: { entity: Order, field: /discount }
operations: [create]
assert: { op: lt, args: [{ var: data.discount }, { var: data.price }] }
severity: error
finding: { code: CMP-004, message: discount.belowPrice }
- id: order.currency.eur
kind: validation
target: { entity: Order, field: /currency }
operations: [create]
assert: { op: eq, args: [{ var: data.currency }, "EUR"] }
severity: error
finding: { code: CMP-005, message: currency.eur }
- id: order.status.not-cancelled
kind: validation
target: { entity: Order, field: /status }
operations: [create]
assert: { op: ne, args: [{ var: data.status }, "cancelled"] }
severity: error
finding: { code: CMP-006, message: status.cancelled }
- id: order.weight.range
kind: validation
target: { entity: Order, field: /weight }
operations: [create]
assert: { op: between, args: [{ var: data.weight }, 0.1, 30] }
severity: error
finding: { code: CMP-007, message: weight.range }
messages:
en:
quantity.positive: "Order at least one item."
quantity.max: "You can order at most 100 items."
price.min: "The price must be at least 1."
discount.belowPrice: "The discount must be lower than the price."
currency.eur: "We only accept orders in EUR."
status.cancelled: "You cannot create a cancelled order."
weight.range: "A parcel weighs between 0.1 and 30 kg."
tests:
- name: zero items, dollars and a heavy parcel are denied
entity: Order
operation: create
given:
data: { quantity: 0, price: 10, discount: 2, currency: "USD", status: "new", weight: 40 }
expect:
decision: deny
findings:
- { rule: order.quantity.positive, fields: [/quantity] }
- { rule: order.currency.eur, fields: [/currency] }
- { rule: order.weight.range, fields: [/weight] }
- name: a valid order is allowed
entity: Order
operation: create
given:
data: { quantity: 3, price: 10, discount: 2, currency: "EUR", status: "new", weight: 2 }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 0,
"price": 10,
"discount": 2,
"currency": "USD",
"status": "new",
"weight": 40
}
}Result, from the engine
Decisiondeny3 findings, server channel
CMP-001errorblockingOrder at least one item./quantityCMP-005errorblockingWe only accept orders in EUR./currencyCMP-007errorblockingA parcel weighs between 0.1 and 30 kg./weight
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.
Common mistakes
lt,lte,gt,gteandbetweenaccept only numbers.{ op: gt, args: ["5", 3] }is an evaluation error, nottrue.eqnever converts:1is not"1". Values of different types are never equal.betweenincludes both ends: 30 is between 0.1 and 30.
Exercise
Write one rule with between that allows 1 to 50 items per order. Check that 0 and 51 are denied
and 50 is allowed.
Hint
between takes the value first, then the lowest and the highest allowed value.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.comparison, version: 1.0.0, title: Comparison operators }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.quantity.range
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: between, args: [{ var: data.quantity }, 1, 50] }
severity: error
finding: { code: CMP-001, message: quantity.range }
messages:
en:
quantity.range: "Order between 1 and 50 items."
tests:
- name: fifty-one items are denied
entity: Order
operation: create
given:
data: { quantity: 51 }
expect:
decision: deny
findings:
- { rule: order.quantity.range, fields: [/quantity] }
- name: zero items are denied
entity: Order
operation: create
given:
data: { quantity: 0 }
expect:
decision: deny
findings:
- { rule: order.quantity.range }
- name: one and fifty items are allowed
entity: Order
operation: create
given:
data: { quantity: 50 }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 51
}
}Result, from the engine
Decisiondeny1 finding, server channel
CMP-001errorblockingOrder between 1 and 50 items./quantity