pico-engine icon indicating copy to clipboard operation
pico-engine copied to clipboard

Let rulesets store information in established subscriptions

Open b1conrad opened this issue 2 years ago • 3 comments

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"]

b1conrad avatar Oct 20 '22 21:10 b1conrad

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
    }
  }

b1conrad avatar Oct 20 '22 22:10 b1conrad

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)
    }
  }

b1conrad avatar Oct 25 '22 14:10 b1conrad

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

b1conrad avatar Aug 23 '23 15:08 b1conrad