LearnOperators
Logic operators
and, or, not and if combine conditions and choose between values.
Logic operators combine booleans. and is true when every argument is true, or when one is.
not turns true into false. if picks its second or third argument, depending on the first.
Syntax
{ op: and, args: [<boolean>, <boolean>, ...] }
{ op: or, args: [<boolean>, <boolean>, ...] }
{ op: not, args: [<boolean>] }
{ op: if, args: [<condition>, <value if true>, <value if false>] }| Operator | Arguments | Result |
|---|---|---|
and, or | any number of booleans | Boolean. Stops at the first argument that decides. and [] is true, or [] is false |
not | boolean | Boolean |
if | boolean, any, any | The second or the third argument. Only that one is evaluated |
Example
The request has a gift message without gift wrap, and asks for express delivery to France.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.logic, version: 1.0.0, title: Logic operators }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.gift-message.needs-wrap
kind: validation
target: { entity: Order, field: /giftMessage }
operations: [create]
assert:
{ op: or, args: [
{ op: eq, args: [{ var: data.giftWrap }, true] },
{ op: eq, args: [{ var: data.giftMessage }, null] }
] }
severity: error
finding: { code: LOG-001, message: gift.needsWrap }
- id: order.express.germany-only
kind: validation
target: { entity: Order, field: /express }
operations: [create]
assert:
op: not
args:
- { op: and, args: [
{ op: eq, args: [{ var: data.express }, true] },
{ op: ne, args: [{ var: data.country }, "DE"] }
] }
severity: error
finding: { code: LOG-002, message: express.germanyOnly }
- id: order.shipping.price
kind: validation
target: { entity: Order, field: /shipping }
operations: [create]
assert:
op: eq
args:
- { var: data.shipping }
- { op: if, args: [{ op: eq, args: [{ var: data.express }, true] }, 9.5, 4.5] }
severity: error
finding: { code: LOG-003, message: shipping.price }
messages:
en:
gift.needsWrap: "A gift message needs gift wrap."
express.germanyOnly: "Express delivery is only available in Germany."
shipping.price: "Shipping costs 4.5, or 9.5 for express delivery."
tests:
- name: a gift message without wrap, express to France
entity: Order
operation: create
given:
data: { giftMessage: "Happy birthday", giftWrap: false, express: true, country: "FR", shipping: 9.5 }
expect:
decision: deny
findings:
- { rule: order.gift-message.needs-wrap }
- { rule: order.express.germany-only }
- name: a wrapped gift, standard shipping
entity: Order
operation: create
given:
data: { giftMessage: "Happy birthday", giftWrap: true, express: false, country: "FR", shipping: 4.5 }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"giftMessage": "Happy birthday",
"giftWrap": false,
"express": true,
"country": "FR",
"shipping": 9.5
}
}Result, from the engine
Decisiondeny2 findings, server channel
LOG-001errorblockingA gift message needs gift wrap./giftMessageLOG-002errorblockingExpress delivery is only available in Germany./express
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.
Common mistakes
- The arguments must be booleans.
{ op: and, args: [1, true] }is an evaluation error. ifneeds exactly three arguments. There is noelse if: nest anotherifin the third.orstops at the firsttrue. Put the cheap or safe test first.
Exercise
Coupons do not apply to express orders. Write a rule that denies an order with a coupon when
express is true. An express order without a coupon is allowed.
Hint
The rule passes when there is no coupon, or when the order is not express. Use or and not.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.logic, version: 1.0.0, title: Logic operators }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
- id: order.coupon.not-express
kind: validation
target: { entity: Order, field: /coupon }
operations: [create]
assert:
op: or
args:
- { op: eq, args: [{ var: data.coupon }, null] }
- { op: not, args: [{ op: eq, args: [{ var: data.express }, true] }] }
severity: error
finding: { code: LOG-001, message: coupon.notExpress }
messages:
en:
coupon.notExpress: "Coupons do not apply to express orders."
tests:
- name: a coupon on an express order is denied
entity: Order
operation: create
given:
data: { coupon: "SAVE10", express: true }
expect:
decision: deny
findings:
- { rule: order.coupon.not-express, fields: [/coupon] }
- name: a coupon on a standard order is allowed
entity: Order
operation: create
given:
data: { coupon: "SAVE10", express: false }
expect: { decision: allow, findings: [] }
- name: express without a coupon is allowed
entity: Order
operation: create
given:
data: { express: true }
expect: { decision: allow, findings: [] }{
"entity": "Order",
"operation": "create",
"data": {
"coupon": "SAVE10",
"express": true
}
}Result, from the engine
Decisiondeny1 finding, server channel
LOG-001errorblockingCoupons do not apply to express orders./coupon