pico-engine
pico-engine copied to clipboard
Let rulesets store information in established subscriptions
Proposing a rule that selects on a wrangler:new_key_value_pair
event, with attributes: Id
the established subscription identifier, key
a key not reserved* for subscriptions, and value
an optional value (a falsy value to mean remove the key).
The rule to raise a wrangler:key_value_updated
event upon successful addition/deletion to/from the established subscription, with the original attributes.
The key-value pair is for the use of the pico that sets it.
For keys that begin either "Rx_" or "Tx_", it is considered good relationship practice to also send a similar event over the subscription to the other side, with the key prefix reversed. Care must be taken to avoid initiating an infinite loop.
*Reserved subscription keys are:
["Id","Rx_role","Tx_role","Rx","Tx","Tx_host"]
Playing with this candidate rule:
select when wrangler new_key_value_pair
Id re#(.+)#
key re#(.+)#
value re#(.*)#
setting(Id,key,value)
pre {
buses = established()
bad_key = ["Id","Rx_role","Tx_role","Rx","Tx","Tx_host"] >< key
index = bad_key => -1 | indexOfId(buses, Id)
ok = index >= 0
add_kv = function(v,i){
i==index => v.put(key,value || null) | v
}
new_established = ok => ent:established.map(add_kv)
| ent:established
}
if ok then noop()
fired {
ent:established := new_established
raise wrangler event "key_value_pair_updated" attributes event:attrs
}
}
Have had success with this rule, testing in localhost:3000
:
rule saveKeyValuePair {
select when wrangler new_key_value_pair
key re#(.+)# value re#(.*)# setting(key,value)
pre {
bus = findBus(established())
invalid = bus.isnull() ||
key.match(re#^(Id|Tx|Rx|Tx_role|Rx_role|wellKnown_Rx|Tx_host)$#)
val = value.decode() || null
already_seen = (bus.keys() >< key && bus{key} == value)
|| (bus{key}.isnull() && val.isnull())
ok = not invalid && not already_seen
index = ok => indexOfId(established(),bus{"Id"}) | -1
new_bus = not ok => null
| value => bus.put(key,value)
| bus.delete(key)
new_key = not ok => null
| key.match(re#^Rx_#) => key.replace(re#^R#,"T")
| key.match(re#^Tx_#) => key.replace(re#^T#,"R")
| key
}
if ok && index >= 0 then
event:send(
{
"eci":bus{"Tx"},"domain":"wrangler","type":"new_key_value_pair",
"attrs":{
"Id":bus{"Id"},
"key":new_key,
"value":new_bus{key}.defaultsTo("")
}
}
, bus{"Tx_host"}
)
fired {
ent:established := ent:established.splice(index,1,new_bus)
}
}
Rather than modifying the io.picolabs.subscription
ruleset, I think it is nearly as easy to have your ruleset "wrap" it and use the relationship Id
to map to the desired auxiliary information. As an example of this, the initiating pico could select on the wrangler:outbound_pending_subscription_added
event as in this wePropose
rule, and the receiving pico could select on the wrangler:inbound_pending_subscription_added
event, as in this theyPropose
rule.
It would also be important to then clean up the layered-on map by selecting when these wrangler
event types come in:
-
outbound_subscription_cancelled
-
outbound_pending_subscription_approved
-
inbound_subscription_cancelled
-
subscription_removed