groups: begin rewrite

This commit is contained in:
Liam Fitzgerald 2020-05-15 08:50:04 +10:00
parent d1d417fb22
commit d2de8d72eb
25 changed files with 1270 additions and 665 deletions

View File

@ -39,7 +39,7 @@
:_ this(invite-created %.y)
:~ (invite-poke:cc [%create /contacts])
[%pass /inv %agent [our.bol %invite-store] %watch /invitatory/contacts]
[%pass /group %agent [our.bol %group-store] %watch /updates]
[%pass /group %agent [our.bol %group-store] %watch /groups]
==
++ on-save !>(state)
++ on-load

View File

@ -1,6 +1,6 @@
:: group-hook: allow syncing group data from foreign paths to local paths
::
/- *group-store, *group-hook
/- *group, store=group-store, hook=group-hook
/+ default-agent, verb, dbug
~% %group-hook-top ..is ~
|%
@ -8,6 +8,7 @@
::
++ versioned-state
$% state-zero
state-one
==
::
::
@ -16,247 +17,47 @@
synced=(map path ship)
==
::
+$ state-one
$: %1
synced=(map group-id ship)
==
::
--
::
=| state-zero
=| state-one
=* state -
::
%- agent:dbug
%+ verb |
^- agent:gall
=<
|_ =bowl:gall
+* this .
group-core +>
gc ~(. group-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= =vase
^- (quip card _this)
=/ old !<(state-zero vase)
:_ this(state old)
%+ murn ~(tap by synced.old)
|= [=path =ship]
^- (unit card)
=/ =wire [(scot %p ship) %group path]
=/ =term ?:(=(our.bowl ship) %group-store %group-hook)
?: (~(has by wex.bowl) [wire ship term]) ~
`[%pass wire %agent [ship term] %watch [%group path]]
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?. ?=(%group-hook-action mark)
(on-poke:def mark vase)
=^ cards state
(poke-group-hook-action:gc !<(group-hook-action vase))
[cards this]
::
++ on-watch
|= =path
^- (quip card _this)
?. ?=([%group @ *] path)
(on-watch:def path)
?. (~(has by synced.state) t.path)
(on-watch:def path)
=/ scry-path=^path
:(welp /=group-store/(scot %da now.bowl) t.path /noun)
=/ grp=(unit group)
.^((unit group) %gx scry-path)
?~ grp
(on-watch:def path)
:_ this
[%give %fact ~ %group-update !>([%path u.grp t.path])]~
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?+ -.sign (on-agent:def wire sign)
::
%watch-ack
?~ p.sign
[~ this]
%- (slog u.p.sign)
?> ?=([@ %group ^] wire)
=/ =ship (slav %p i.wire)
=* group t.t.wire
:: only remove from synced if this watch-nack came from the ship we
:: thought we were actively syncing from
::
=? synced.state
=(ship (~(gut by synced.state) group ship))
(~(del by synced.state) group)
[~ this]
::
%kick
?> ?=([@ %group ^] wire)
=/ =ship (slav %p i.wire)
=* group t.t.wire
?. (~(has by synced.state) group)
[~ this]
=* group-path t.wire
:_ this
[%pass wire %agent [ship %group-hook] %watch group-path]~
::
%fact
?. ?=(%group-update p.cage.sign)
(on-agent:def wire sign)
=^ cards state
?: (team:title our.bowl src.bowl)
(handle-local:gc !<(group-update q.cage.sign))
(handle-foreign:gc !<(group-update q.cage.sign))
[cards this]
==
--
|_ =bowl:gall
+* this .
group-core +>
gc ~(. group-core bowl)
def ~(. (default-agent this %|) bowl)
::
|_ bol=bowl:gall
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= =vase
^- (quip card _this)
=/ old !<(state-one vase)
:_ this(state old)
%+ murn ~(tap by synced.old)
|= [=path =ship]
^- (unit card)
=/ =wire [(scot %p ship) %group path]
=/ =term ?:(=(our.bowl ship) %group-store %group-hook)
?: (~(has by wex.bowl) [wire ship term]) ~
`[%pass wire %agent [ship term] %watch [%group path]]
::
++ poke-group-hook-action
|= act=group-hook-action
^- (quip card _state)
?- -.act
%add
?. (team:title our.bol src.bol)
[~ state]
=/ group-path [%group path.act]
=/ group-wire [(scot %p ship.act) group-path]
?: (~(has by synced.state) path.act)
[~ state]
=. synced.state (~(put by synced.state) path.act ship.act)
:_ state
?: =(ship.act our.bol)
[%pass group-wire %agent [ship.act %group-store] %watch group-path]~
[%pass group-wire %agent [ship.act %group-hook] %watch group-path]~
::
%remove
=/ ship (~(get by synced.state) path.act)
?~ ship
[~ state]
?: &(=(u.ship our.bol) (team:title our.bol src.bol))
:: delete one of our own paths
=/ group-wire [(scot %p our.bol) %group path.act]
:_ state(synced (~(del by synced.state) path.act))
%+ snoc
(pull-wire group-wire path.act)
[%give %kick [%group path.act]~ ~]
?: |(=(u.ship src.bol) (team:title our.bol src.bol))
:: delete a foreign ship's path
=/ group-wire [(scot %p u.ship) %group path.act]
:_ state(synced (~(del by synced.state) path.act))
(pull-wire group-wire path.act)
:: don't allow
[~ state]
==
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
::
++ handle-local
|= diff=group-update
^- (quip card _state)
?- -.diff
%initial [~ state]
%keys [~ state]
%path [~ state]
%bundle [~ state]
%add [(update-subscribers [%group pax.diff] diff) state]
%remove [(update-subscribers [%group pax.diff] diff) state]
::
%unbundle
=/ ship (~(get by synced.state) pax.diff)
?~ ship [~ state]
(poke-group-hook-action [%remove pax.diff])
==
::
++ handle-foreign
|= diff=group-update
^- (quip card _state)
?- -.diff
%initial [~ state]
%keys [~ state]
%bundle [~ state]
%path
:_ state
?~ pax.diff ~
=/ ship (~(get by synced.state) pax.diff)
?~ ship ~
?. =(src.bol u.ship) ~
=/ have-group=(unit group)
(group-scry pax.diff)
?~ have-group
:: if we don't have the group yet, create it
::
:~ (group-poke pax.diff [%bundle pax.diff])
(group-poke pax.diff [%add members.diff pax.diff])
==
:: if we already have the group, calculate and apply the diff
::
=/ added=group (~(dif in members.diff) u.have-group)
=/ removed=group (~(dif in u.have-group) members.diff)
%+ weld
?~ added ~
[(group-poke pax.diff [%add added pax.diff])]~
?~ removed ~
[(group-poke pax.diff [%remove removed pax.diff])]~
::
%add
:_ state
?~ pax.diff ~
=/ ship (~(get by synced.state) pax.diff)
?~ ship ~
?. =(src.bol u.ship) ~
[(group-poke pax.diff diff)]~
::
%remove
?~ pax.diff [~ state]
=/ ship (~(get by synced.state) pax.diff)
?~ ship [~ state]
?. =(src.bol u.ship) [~ state]
?. (~(has in members.diff) our.bol)
:_ state
[(group-poke pax.diff diff)]~
=/ changes (poke-group-hook-action [%remove pax.diff])
:_ +.changes
%+ welp -.changes
:~ (group-poke pax.diff diff)
(group-poke pax.diff [%unbundle pax.diff])
==
::
%unbundle
?~ pax.diff [~ state]
=/ ship (~(get by synced.state) pax.diff)
?~ ship [~ state]
?. =(src.bol u.ship) [~ state]
(poke-group-hook-action [%remove pax.diff])
==
::
++ group-poke
|= [pax=path action=group-action]
^- card
[%pass pax %agent [our.bol %group-store] %poke %group-action !>(action)]
::
++ group-scry
|= pax=path
^- (unit group)
.^((unit group) %gx ;:(weld /=group-store/(scot %da now.bol) pax /noun))
::
++ update-subscribers
|= [pax=path diff=group-update]
^- (list card)
[%give %fact ~[pax] %group-update !>(diff)]~
::
++ pull-wire
|= [wir=wire pax=path]
^- (list card)
=/ shp (~(get by synced.state) pax)
?~ shp
~
?: =(u.shp our.bol)
[%pass wir %agent [our.bol %group-store] %leave ~]~
[%pass wir %agent [u.shp %group-hook] %leave ~]~
++ on-poke on-poke:def
++ on-watch on-watch:def
++ on-agent on-agent:def
>>>>>>> 457bd4c3a... groups: begin rewrite
--

View File

@ -0,0 +1,192 @@
:: group-listen-hook: receive group updates
::
::
::
/- *group, hook=group-hook
/+ dbug, verb, store=group-store, default-agent
|%
+$ card card:agent:gall
+$ versioned-state
$% state-zero
==
+$ state-zero
$: %0
listening=(set group-id)
==
--
=| state-zero
=* state -
::
%- agent:dbug
%+ verb |
^- agent:gall
=<
|_ =bowl:gall
+* this .
group-core +>
gc ~(. group-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= =vase
^- (quip card _this)
=/ old !<(state-zero vase)
`this(state old)
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?. ?=(%group-hook-action mark)
(on-poke:def mark vase)
=^ cards state
(poke-hook-action:gc !<(action:hook vase))
[cards this]
::
++ on-watch on-watch:def
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
=^ cards state
?+ -.wire ~|([dap.bowl %weird-agent-wire wire] !!)
%store (take-store-sign sign)
%proxy (take-proxy-sign +.wire sign)
==
[cards this]
--
|_ bol=bowl:gall
:: +poke-hook-action: Start/stop syncing a foreign group
::
++ poke-hook-action
|= =action:hook
^- (quip card _state)
|^
?- -.action
%add (add +.action)
%remove (remove +.action)
==
++ add
|= =group-id
^- (quip card _state)
?: (~(has in listening) group-id)
`state
=. listening
(~(put in listening) group-id)
=/ group-path
(group-id:en-path:store group-id)
:_ state
%+ weld
(listen-group group-id)
(add-self group-id)
++ remove
|= =group-id
^- (quip card _state)
?. (~(has in listening) group-id)
`state
=. listening
(~(del in listening) group-id)
:_ state
(leave-group group-id)
--
:: +take-store-sign: take sign from %group-store
::
:: We only poke %group-store to remove ourselves when we leave a channel.
++ take-store-sign
|= =sign:agent:gall
^- (quip card _state)
?- -.sign
%watch-ack [~ state]
%kick [~ state]
%fact [~ state]
%poke-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bol)} failed poke to group-store"
%- (slog tank u.p.sign)
[~ state]
==
:: +take-proxy-sign: take sign from %group-proxy-hook
::
++ take-proxy-sign
|= [=wire =sign:agent:gall]
^- (quip card _state)
=/ =group-id
~| "bad proxy wire: {<wire>}"
~| <sign>
(need (group-id:de-path:store wire))
?- -.sign
%kick [(listen-group group-id) state]
::
%poke-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bol)} failed poke to group-proxy!"
%- (slog tank u.p.sign)
[~ state]
::
%watch-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bol)} failed subscribe to group-proxy!"
%- (slog tank u.p.sign)
[(store-leave-group group-id) state]
::
%fact
?> ?=(%group-update p.cage.sign)
(take-proxy-update wire !<(update:store q.cage.sign))
==
::
:: +take-proxy-update: Handle new update from %group-proxy-hook
::
++ take-proxy-update
|= [=wire =update:store]
^- (quip card _state)
?: ?=(%initial -.update)
[~ state]
:_ state
[%pass [%listen wire] %agent [our.bol %group-store] %poke %group-update !>(update)]~
:: +listen-group: Start a new subscription to the proxied .group-id
::
++ listen-group
|= =group-id
^- (list card)
=/ pax=path
(group-id:en-path:store group-id)
[%pass [%listen pax] %agent [ship.group-id %group-proxy-hook] %watch [%groups pax]]~
:: +add-self: Add self to group
++ add-self
|= =group-id
^- (list card)
=/ pax=path
(group-id:en-path:store group-id)
[%pass [%listen pax] %agent [ship.group-id %group-proxy-hook] %poke %group-action !>([%add-members group-id (sy our.bol ~) ~])]~
:: +leave-group: Leave a foreign group
++ leave-group
|= =group-id
^- (list card)
=/ pax=path
(group-id:en-path:store group-id)
:~ [%pass [%listen pax] %agent [ship.group-id %group-proxy-hook] %leave ~]
[%pass [%store pax] %agent [our.bol %group-store] %poke %group-update !>([%remove-group group-id ~])]
[%pass [%listen pax] %agent [ship.group-id %group-proxy-hook] %poke %group-action !>([%remove-members group-id (sy our.bol ~)])]
==
:: +store-leave-group: Remove a foreign group from our group-store
++ store-leave-group
|= =group-id
^- (list card)
=/ pax=path
(group-id:en-path:store group-id)
[%pass [%store pax] %agent [our.bol %group-store] %poke %group-update !>([%remove-group group-id ~])]~
::
--

View File

@ -0,0 +1,293 @@
:: group-proxy-hook: propagate group updates
::
:: This hook relays foreign subscriptions and pokes into local stores, if
:: permissions are met.
::
:: Subscriptions:
:: - /groups/[group-id]: updates for a particular group
::
:: Pokes:
:: - %group-update: Proxy update to local group update. Crashes if permissions
:: checks fail.
::
/- *group, hook=group-hook
/+ dbug, verb, store=group-store, default-agent
|%
+$ card card:agent:gall
+$ state-zero
$: %0
proxied=(jug path ship)
==
+$ versioned-state
$% state-zero
==
--
::
=| state-zero
=* state -
%- agent:dbug
%+ verb |
^- agent:gall
=<
|_ =bowl:gall
+* this .
group-core +>
gc ~(. group-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init
:_ this
:~ [%pass / %arvo %d %flog %text "{(trip dap.bowl)} started"]
[%pass /store %agent [our.bowl %group-store] %watch /groups]
==
++ on-save !>(state)
++ on-load
|= =vase
`this(state !<(state-zero vase))
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
::
++ on-leave
|= =path
^- (quip card _this)
=^ cards state
(stop-proxy:gc src.bowl path)
[cards this]
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
=^ cards state
?+ mark (on-poke:def mark vase)
%group-action (poke-group-update:gc !<(action:store vase))
==
[cards this]
::
++ on-watch
|= =path
^- (quip card _this)
?< (team:title [our src]:bowl)
?> ?=([%groups *] path)
?> (permitted:gc src.bowl t.path)
=^ cards state
(start-proxy:gc src.bowl t.path)
[cards this]
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
=^ cards state
?+ -.wire ~|([dap.bowl %bad-agent-wire wire] !!)
%store (take-store-sign sign)
%listen (take-listen-sign sign)
==
[cards this]
--
|_ bol=bowl:gall
:: +should-proxy-poke: Check if poke should be proxied
::
:: We only allow users to add and remove themselves.
++ should-proxy-poke
|= =update:store
^- ?
?: ?=(%add-members -.update)
=(~(tap in ships.update) ~[src.bol])
?: ?=(%remove-members -.update)
=(~(tap in ships.update) ~[src.bol])
%.n
:: +poke-group-update: Proxy poke to %group-store
::
:: Only proxy pokes if permissions are correct and we host the group.
::
++ poke-group-update
|= =update:store
^- (quip card _state)
?: ?=(%initial -.update)
[~ state]
?> =(ship.group-id.update our.bol)
?> (should-proxy-poke update)
=/ =path
(group-id:en-path:store group-id.update)
?> (permitted src.bol path)
:_ state
[%pass [%store path] %agent [our.bol %group-store] %poke %group-update !>(update)]~
:: +take-listen-sign: Handle incoming sign from %group-listen-hook
::
:: group-listen-hook doesn't send us anything except pokes
++ take-listen-sign
|= =sign:agent:gall
^- (quip card _state)
?- -.sign
%kick [~ state]
%watch-ack [~ state]
%fact [~ state]
::
%poke-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bol)} failed poke to group-listen. very wrong!"
%- (slog tank u.p.sign)
[~ state]
==
:: +take-store-sign: Handle incoming sign from %group-store
::
:: group-store should send us all its store updates over a subscription on
:: /groups. We also proxy pokes to it.
::
++ take-store-sign
|= =sign:agent:gall
^- (quip card _state)
?- -.sign
%kick [watch-store state]
::
%poke-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bol)} failed poke to group-store"
%- (slog tank u.p.sign)
[~ state]
::
%watch-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bol)} failed subscribe to group-store. very wrong!"
%- (slog tank u.p.sign)
[~ state]
::
%fact
?+ p.cage.sign ~|("{<dap.bol>} unexpected mark: {<p.cage.sign>}" !!)
%group-initial [~ state]
%group-update (take-store-update !<(update:store q.cage.sign))
==
==
:: +take-store-update: Handle new %fact from %group-store
::
:: We forward the update onto the correct path, and recalculate permissions,
:: kicking any subscriptions whose permissions have been revoked.
::
++ take-store-update
|= =update:store
^- (quip card _state)
?: ?=(%initial -.update)
`state
=/ =path
(group-id:en-path:store group-id.update)
?. (~(has by proxied) path)
[~ state]
=^ cards state
(handle-revocations update path)
:_ state
:- [%give %fact [%groups path]~ %group-update !>(update)]
cards
::
:: +handle-revocations: Handle revoked permissions from a update:store
::
:: If the mutation to the group store has impacted permissions, then kick
:: relevant subscriptions and remove them from our state
::
++ handle-revocations
|= [=update:store =path]
^- (quip card _state)
|^
?: ?=(%remove-group -.update)
=. proxied
(~(del by proxied) group-id.update)
:_ state
[%give %kick [%groups path]~ ~]~
?: ?=(%change-policy -.update)
(handle-permissions-change diff.update)
?. ?=(%remove-members -.update)
[~ state]
(revoked-permissions ships.update)
::
++ handle-permissions-change
|= =diff:policy
^- (quip card _state)
?+ -.diff [~ state]
%ban-ships (revoked-permissions ships.diff)
==
::
++ revoked-permissions
|= ships=(set ship)
^- (quip card _state)
=/ to-kick=(list ship)
~(tap in ships)
=/ subs=(set ship)
(~(gut by proxied) path ~)
=| cards=(list card)
|-
?~ to-kick
[cards state]
=. proxied
(~(del ju proxied) path i.to-kick)
=. cards
[[%give %kick [%groups path]~ `i.to-kick] cards]
$(to-kick t.to-kick)
--
:: +start-proxy: Start proxying .path to .who
::
++ start-proxy
|= [who=ship =path]
^- (quip card _state)
=. proxied
(~(put ju proxied) path who)
[(give-initial path) state]
:: +stop-proxy: Stop proxying .path to .who
::
++ stop-proxy
|= [who=ship =path]
^- (quip card _state)
=. proxied
(~(del ju proxied) path who)
`state
:: +watch-store: Watch group-store for changes
::
++ watch-store
^- (list card)
[%pass /group-store %agent [our.bol %group-store] %watch /groups]~
:: +permitted: check if .ship can access .path
::
++ permitted
|= [=ship =path]
^- ?
?> ?=([@ @ *] path)
=/ u-group-id
(group-id:de-path:store path)
?~ u-group-id
%.n
=* group-id u.u-group-id
=/ pax=^path
(welp [%groups path] /permitted/[(scot %p ship)])
(scry-store ? pax)
:: +give-initial: give initial state for .path
::
:: Must be called in +on-watch. No-ops if the group does not exist yet
++ give-initial
|= =path
^- (list card)
=/ u-group
(scry-store (unit group) [%groups path])
?~ u-group ~
=* group u.u-group
=/ =group-id
(need (group-id:de-path:store path))
=/ =cage
:- %group-update
!> ^- update:store
[%initial-group group-id group]
[%give %fact ~ cage]~
::
++ scry-store
|* [=mold =path]
.^ mold
%gx
(scot %p our.bol)
%group-store
(scot %da now.bol)
(welp path /noun)
==
--

View File

@ -1,23 +1,64 @@
:: group-store: data store for groups of ships
:: group-store: Store groups of ships
::
/- *group-store
/+ default-agent, verb, dbug
:: group-store stores groups of ships, so that resources in other apps can be
:: associated with a group. The current model of group-store rolls
:: permissions and invites inside this store for simplicity reasons, although
:: these should be prised apart in a future revision of group store.
::
:: ## Scry paths
::
:: /y/groups:
:: A listing of the current groups
:: /y/groups/[group-id]/tag-queries:
:: A listing of the tag queries for a group
:: /x/groups/[group-id]:
:: The group itself
:: /x/groups/[group-id]/tag-queries/[tag]:
:: The subset with tag
:: /x/groups/[group-id]/permitted/[ship]:
:: A flag indicated if the ship is permitted
::
::
:: ## Subscription paths
::
:: /groups:
:: A stream of the current updates to the state, sending the initial state
:: upon subscribe.
::
:: ## Pokes
::
:: %group-action:
:: Modify the group. Further documented in /sur/group-store.hoon
::
::
/- *group
/+ store=group-store, default-agent, verb, dbug
|%
+$ card card:agent:gall
::
+$ versioned-state
$% state-zero
state-one
==
::
+$ state-zero
$: %0
* :: =groups
==
::
::
+$ state-one
$: %1
=groups
==
::
+$ diff [%group-update group-update]
+$ diff
$% [%group-update update:store]
[%group-initial groups]
==
--
::
=| state-zero
=| state-one
=* state -
::
%- agent:dbug
@ -30,19 +71,24 @@
gc ~(. group-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-init
:_ this
[%pass / %arvo %d %flog %text "{(trip dap.bowl)} started"]~
++ on-save !>(state)
++ on-load
|= old=vase
`this(state !<(state-zero old))
|= =old=vase
=/ old !<(versioned-state old-vase)
?. ?=(%1 -.old)
`this
`this(state old)
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?> (team:title our.bowl src.bowl)
=^ cards state
?: ?=(%group-action mark)
(poke-group-action:gc !<(group-action vase))
?: ?=(%group-update mark)
(poke-group-update:gc !<(update:store vase))
(on-poke:def mark vase)
[cards this]
::
@ -50,22 +96,9 @@
|= =path
^- (quip card _this)
?> (team:title our.bowl src.bowl)
|^
=/ cards=(list card)
?+ path (on-watch:def path)
[%all ~] (give %group-update !>([%initial groups]))
[%updates ~] ~
[%keys ~] (give %group-update !>([%keys ~(key by groups)]))
[%group *]
(give %group-update !>([%path (~(got by groups) t.path) t.path]))
==
[cards this]
::
++ give
|= =cage
^- (list card)
[%give %fact ~ cage]~
--
?> ?=([%groups ~] path)
:_ this
[%give %fact ~ %group-initial !>(groups)]~
::
++ on-leave on-leave:def
::
@ -73,7 +106,13 @@
|= =path
^- (unit (unit cage))
?+ path (on-peek:def path)
[%x *] ``noun+!>((~(get by groups) t.path))
[%x %groups @ @ ~] ``noun+!>((peek-group t.t.path))
::
[%x %groups @ @ %permitted @ ~]
``noun+!>((peek-group-permitted t.t.path (slav %p i.t.t.t.t.t.path)))
::
[%x %groups @ @ %permission-role @ ~]
``noun+!>((peek-group-permitted t.t.path (slav %p i.t.t.t.t.t.path)))
==
::
++ on-agent on-agent:def
@ -82,85 +121,257 @@
--
::
|_ bol=bowl:gall
++ ship-rank
|= =ship
^- rank
=/ size
(met 3 ship)
?: (lte size 2)
%0
?: (lte size 4)
%1
?: (lte size 8)
%2
%3
++ peek-group-permitted
|= [=path =ship]
^- ?
=/ maybe-group
(peek-group path)
?~ maybe-group
%.n
=* group u.maybe-group
=* policy policy.group
?- -.policy
%invite
|((~(has in pending.policy) ship) (~(has in members.group) ship))
%open
&(!(~(has in banned.policy) ship) (~(has in ranks.policy) (ship-rank ship)))
==
::
++ poke-group-action
|= action=group-action
++ peek-group
|= =path
^- (unit group)
=/ m-group-id
(group-id:de-path:store path)
?~ m-group-id ~
=* group-id u.m-group-id
(~(get by groups) group-id)
::
++ poke-group-update
|= =update:store
^- (quip card _state)
?> (team:title our.bol src.bol)
?- -.action
%add (handle-add action)
%remove (handle-remove action)
%bundle (handle-bundle action)
%unbundle (handle-unbundle action)
|^
?- -.update
%add-group (add-group +.update)
%add-members (add-members +.update)
%remove-members (remove-members +.update)
%add-tag (add-tag +.update)
%remove-tag (remove-tag +.update)
%change-policy (change-policy +.update)
%remove-group (remove-group +.update)
%initial-group (initial-group +.update)
%initial [~ state]
==
:: +add-group: add group to store
::
:: no-op if group-already exists
::
++ add-group
|= [=group-id =group]
^- (quip card _state)
?: (~(has by groups) group-id)
[~ state]
=. groups
(~(put by groups) group-id group)
:_ state
(send-diff %add-group group-id group)
:: +add-members: add members to group
::
:: no-op if group does not exist
::
++ add-members
|= [=group-id new-ships=(set ship) tags=(set tag)]
^- (quip card _state)
?. (~(has by groups) group-id)
[~ state]
=/ =group (~(got by groups) group-id)
=. members.group (~(uni in members.group) new-ships)
=. tag-queries.group
(merge-tags tag-queries.group new-ships tags)
=. groups
(~(put by groups) group-id group)
:_ state
(send-diff %add-members group-id new-ships tags)
:: +remove-members: remove members from group
::
:: no-op if group does not exist
:: TODO: remove tags as well
::
++ remove-members
|= [=group-id ships=(set ship)]
^- (quip card _state)
?. (~(has by groups) group-id)
[~ state]
=/ =group
(~(got by groups) group-id)
=. members.group
(~(dif in members.group) ships)
=. groups
(~(put by groups) group-id group)
:_ state
(send-diff %remove-members group-id ships)
:: +add-tag: add tag to ships
::
:: no-op if group does not exist
:: crash if ships are not in group (is this right?)
::
++ add-tag
|= [=group-id =tag ships=(set ship)]
^- (quip card _state)
?: (~(has by groups) group-id)
[~ state]
=/ =group
(~(got by groups) group-id)
?> ?=(~ (~(dif in ships) members.group))
=. tag-queries.group
(merge-tags tag-queries.group ships (sy tag ~))
=. groups
(~(put by groups) group-id group)
:_ state
(send-diff %add-tag group-id tag ships)
:: +remove-tag: remove tag from ships
::
:: no-op if group does not exist
:: crash if ships are not in group or tag does not exist (is this right?)
::
++ remove-tag
|= [=group-id =tag ships=(set ship)]
^- (quip card _state)
?: (~(has by groups) group-id)
[~ state]
=/ =group
(~(got by groups) group-id)
?> ?& ?=(~ (~(dif in ships) members.group))
(~(has by tag-queries.group) tag)
==
=/ tag-query
(~(got by tag-queries.group) tag)
=. tag-query
(~(dif in tag-query) ships)
=. tag-queries.group
(~(put by tag-queries.group) tag tag-query)
:_ state
(send-diff %remove-tag group-id tag ships)
:: initial-group: initialize foreign group
::
++ initial-group
|= [=group-id =group]
^- (quip card _state)
=. groups
(~(put by groups) group-id group)
:_ state
(send-diff %initial-group group-id group)
:: +change-policy: modify group access control
::
::
++ change-policy
|= [=group-id =diff:policy]
^- (quip card _state)
?. (~(has by groups) group-id)
[~ state]
=/ =group
(~(got by groups) group-id)
=* policy policy.group
|^
=. policy
=- ~& - -
?+ -.diff !!
%allow-ranks (allow-ranks +.diff)
%ban-ranks (ban-ranks +.diff)
%allow-ships (allow-ships +.diff)
%ban-ships (ban-ships +.diff)
==
=. groups
(~(put by groups) group-id group)
:_ state
(send-diff %change-policy group-id diff)
::
++ allow-ranks
|= ranks=(set rank)
^- ^policy
?> ?=(%open -.policy)
=. ranks.policy
(~(uni in ranks.policy) ranks)
policy
::
++ ban-ranks
|= ranks=(set rank)
^- ^policy
?> ?=(%open -.policy)
=. ranks.policy
(~(dif in ranks.policy) ranks)
policy
::
++ allow-ships
|= ships=(set ship)
^- ^policy
?> ?=(%open -.policy)
=. banned.policy
(~(dif in banned.policy) ships)
policy
::
++ ban-ships
|= ships=(set ship)
^- ^policy
?> ?=(%open -.policy)
=. banned.policy
(~(uni in banned.policy) ships)
=. members.group
(~(dif in members.group) banned.policy)
policy
--
:: +remove-group: remove group from store
::
:: no-op if group does not exist
++ remove-group
|= [=group-id ~]
^- (quip card _state)
?. (~(has by groups) group-id)
`state
=. groups
(~(del by groups) group-id)
:_ state
(send-diff %remove-group group-id ~)
::
--
:: +merge-tags: merge tags
++ merge-tags
|= [=tag-queries ships=(set ship) tags=(set tag)]
^+ tag-queries
=/ tags ~(tap in tags)
|-
?~ tags
tag-queries
=* tag i.tags
=/ current-query=(set ship)
(~(gut by tag-queries) tag ~)
%= $
tags t.tags
::
tag-queries
%+ ~(put by tag-queries)
tag
(~(uni in current-query) ships)
==
:: +send-diff: update subscribers of new state
::
++ handle-add
|= act=group-action
^- (quip card _state)
?> ?=(%add -.act)
?~ pax.act
[~ state]
?. (~(has by groups) pax.act)
[~ state]
=/ members (~(got by groups) pax.act)
=. members (~(uni in members) members.act)
?: =(members (~(got by groups) pax.act))
[~ state]
:- (send-diff pax.act act)
state(groups (~(put by groups) pax.act members))
::
++ handle-remove
|= act=group-action
^- (quip card _state)
?> ?=(%remove -.act)
?~ pax.act
[~ state]
?. (~(has by groups) pax.act)
[~ state]
=/ members (~(got by groups) pax.act)
=. members (~(dif in members) members.act)
?: =(members (~(got by groups) pax.act))
[~ state]
:- (send-diff pax.act act)
state(groups (~(put by groups) pax.act members))
::
++ handle-bundle
|= act=group-action
^- (quip card _state)
?> ?=(%bundle -.act)
?~ pax.act
[~ state]
?: (~(has by groups) pax.act)
[~ state]
:- (send-diff pax.act act)
state(groups (~(put by groups) pax.act *group))
::
++ handle-unbundle
|= act=group-action
^- (quip card _state)
?> ?=(%unbundle -.act)
?~ pax.act
[~ state]
?. (~(has by groups) pax.act)
[~ state]
:- (send-diff pax.act act)
state(groups (~(del by groups) pax.act))
::
++ update-subscribers
|= [pax=path act=group-action]
^- (list card)
[%give %fact ~[pax] %group-update !>(act)]~
::
:: We only allow subscriptions on /groups
:: so just give the fact there.
++ send-diff
|= [pax=path act=group-action]
|= =update:store
^- (list card)
%- zing
:~ (update-subscribers /all act)
(update-subscribers /updates act)
(update-subscribers [%group pax] act)
?. |(=(%bundle -.act) =(%unbundle -.act))
~
(update-subscribers /keys act)
==
[%give %fact ~[/groups] %group-update !>(update)]~
::
--

View File

@ -27,212 +27,23 @@
%+ verb |
%- agent:dbug
^- agent:gall
=<
|_ =bowl:gall
+* this .
do ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= old=vase
^- (quip card _this)
[~ this(state !<(state-0 old))]
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?+ mark (on-poke:def mark vase)
%json
:: only accept json from the host team
::
?> (team:title our.bowl src.bowl)
=^ cards state
%- handle-action:do
%- json-to-perm-group-hook-action
!<(json vase)
[cards this]
::
%permission-group-hook-action
=^ cards state
%- handle-action:do
!<(permission-group-hook-action vase)
[cards this]
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?. ?=([%group *] wire)
(on-agent:def wire sign)
?- -.sign
%poke-ack ~|([dap.bowl %unexpected-poke-ack wire] !!)
::
%kick
:_ this
[(watch-group:do t.wire)]~
::
%watch-ack
?~ p.sign [~ this]
=/ =tank leaf+"{(trip dap.bowl)} failed subscribe at {(spud wire)}"
%- (slog tank u.p.sign)
[~ this(relation (~(del by relation) t.wire))]
::
%fact
?. ?=(%group-update p.cage.sign)
(on-agent:def wire sign)
=^ cards state
%- handle-group-update:do
!<(group-update q.cage.sign)
[cards this]
==
::
++ on-peek on-peek:def
++ on-watch on-watch:def
++ on-leave on-leave:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--
::
|_ =bowl:gall
++ handle-action
|= act=permission-group-hook-action
^- (quip card _state)
?> (team:title our.bowl src.bowl)
?- -.act
%associate (handle-associate group.act permissions.act)
%dissociate (handle-dissociate group.act permissions.act)
==
+* this .
do ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
::
++ handle-associate
|= [group=group-path associate=(set [permission-path kind])]
^- (quip card _state)
=/ perms (~(get by relation) group)
:: if relation does not exist, create it and subscribe.
=/ perm-paths=(set path)
(~(run in associate) head)
?~ perms
:_ state(relation (~(put by relation) group perm-paths))
(snoc (recreate-permissions perm-paths associate) (watch-group group))
::
=/ grp (group-scry group)
=. u.perms (~(uni in u.perms) perm-paths)
:_ state(relation (~(put by relation) group u.perms))
%+ weld
(recreate-permissions perm-paths associate)
?~ grp
~
(add-members group u.grp u.perms)
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= old=vase
^- (quip card _this)
[~ this(state !<(state-0 old))]
::
++ handle-dissociate
|= [group=path remove=(set permission-path)]
^- (quip card _state)
=/ perms=(set permission-path)
(fall (~(get by relation) group) *(set permission-path))
?: =(~ perms)
[~ state]
:: remove what we must. if that means we are no longer mirroring this group
:: into any permissions, remove it from state entirely.
::
=. perms (~(del in perms) remove)
?~ perms
:_ state(relation (~(del by relation) group))
[(group-pull group)]~
[~ state(relation (~(put by relation) group perms))]
::
++ handle-group-update
|= diff=group-update
^- (quip card _state)
?- -.diff
%initial [~ state]
%keys [~ state]
%bundle [~ state]
::
%path
:: set all permissions paths
=/ perms (~(got by relation) pax.diff)
:_ state
(add-members pax.diff members.diff perms)
::
%add
:: set all permissions paths
=/ perms (~(get by relation) pax.diff)
?~ perms
[~ state]
:_ state
%+ turn ~(tap in u.perms)
|= =path
(permission-poke path [%add path members.diff])
::
%remove
:: set all permissions paths
=/ perms (~(get by relation) pax.diff)
?~ perms
[~ state]
:_ state
%+ turn ~(tap in u.perms)
|= =path
(permission-poke path [%remove path members.diff])
::
%unbundle
:: pull subscriptions
=/ perms (~(get by relation) pax.diff)
?~ perms
:_ state(relation (~(del by relation) pax.diff))
[(group-pull pax.diff)]~
:_ state(relation (~(del by relation) pax.diff))
:- (group-pull pax.diff)
%+ turn ~(tap in u.perms)
|= =path
(permission-poke path [%delete path])
==
::
++ permission-poke
|= [=wire action=permission-action]
^- card
:* %pass
[%write wire]
%agent
[our.bowl %permission-store]
%poke
[%permission-action !>(action)]
==
::
++ group-scry
|= pax=path
^- (unit group)
.^((unit group) %gx ;:(weld /=group-store/(scot %da now.bowl) pax /noun))
::
++ add-members
|= [pax=path mem=(set ship) perms=(set path)]
^- (list card)
%+ turn ~(tap in perms)
|= =path
(permission-poke path [%add path mem])
::
++ recreate-permissions
|= [perm-paths=(set path) associate=(set [permission-path kind])]
^- (list card)
%+ weld
%+ turn ~(tap in perm-paths)
|= =path
(permission-poke path [%delete path])
%+ turn ~(tap in associate)
|= [=path =kind]
=| pem=permission
=. kind.pem kind
(permission-poke path [%create path pem])
::
::
++ watch-group
|= =group-path
^- card
=. group-path [%group group-path]
[%pass group-path %agent [our.bowl %group-store] %watch group-path]
::
++ group-pull
|= =group-path
^- card
[%pass [%group group-path] %agent [our.bowl %group-store] %leave ~]
++ on-poke on-poke:def
++ on-agent on-agent:def
++ on-peek on-peek:def
++ on-watch on-watch:def
++ on-leave on-leave:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--

View File

@ -0,0 +1,10 @@
:: group-store|join: initialize a group
::
/- *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ~] ~]
==
:- %group-action
^- action
[%add ship term]

View File

@ -0,0 +1,10 @@
:: group-listen-hook|add: add a group
::
/- *group, *group-hook
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ~] ~]
==
:- %group-hook-action
^- action
[%add ship term]

View File

@ -0,0 +1,10 @@
:: group-listen-hook|remove: add a group
::
/- *group, *group-hook
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ~] ~]
==
:- %group-hook-action
^- action
[%remove ship term]

View File

@ -1,10 +1,10 @@
:: group-store|add: add members to a group
::
/- *group-store
/- *group, *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=path members=(list ship) ~] ~]
[[=ship =term ships=(list ship) ~] ~]
==
:- %group-action
^- group-action
[%add (sy members) path]
^- action
[%add-members [ship term] (sy ships) ~]

View File

@ -0,0 +1,10 @@
:: group-store|allow-ranks: allow ranks for group
::
/- *group, *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ranks=(list rank) ~] ~]
==
:- %group-update
^- action
[%change-policy [ship term] %allow-ranks (sy ranks)]

View File

@ -0,0 +1,10 @@
:: group-store|allow-ships: remove ships from banlist
::
/- *group, *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ships=(list ship) ~] ~]
==
:- %group-update
^- action
[%change-policy [ship term] %allow-ships (sy ships)]

View File

@ -0,0 +1,10 @@
:: group-store|allow-ranks: allow ranks for group
::
/- *group, *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ranks=(list rank) ~] ~]
==
:- %group-update
^- action
[%change-policy [ship term] %ban-ranks (sy ranks)]

View File

@ -0,0 +1,10 @@
:: group-store|ban-ships: ban members from a group
::
/- *group, *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ships=(list ship) ~] ~]
==
:- %group-update
^- action
[%change-policy [ship term] %ban-ships (sy ships)]

View File

@ -3,8 +3,8 @@
/- *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=path ~] ~]
[[=term ~] ~]
==
:- %group-action
^- group-action
[%bundle path]
:- %group-update
^- action
[%add-group [p.beak term] (sy p.beak ~) ~ %open ~ ~]

View File

@ -0,0 +1,10 @@
:: group-store|join: join a group
::
/- *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=ship =term ~] ~]
==
:- %group-action
^- action
[%join-group ship term]

View File

@ -3,8 +3,8 @@
/- *group-store
:- %say
|= $: [now=@da eny=@uvJ =beak]
[[=path members=(list ship) ~] ~]
[[=ship =term ships=(list ship) ~] ~]
==
:- %group-action
^- group-action
[%remove (sy members) path]
^- action
[%remove-members [p.beak term] (sy ships)]

View File

@ -0,0 +1,32 @@
/- *group, sur=group-store
^?
=< [. sur]
=, sur
|%
:: +en-path: transform into path
::
++ en-path
|%
::
++ group-id
|= ^group-id
^- path
/[(scot %p ship)]/[term]
--
:: +de-path: transform from path
::
++ de-path
|%
::
++ group-id
|= =path
^- (unit ^group-id)
?. ?=([@ @ *] path)
~
=/ ship=(unit ship)
(slaw %p i.path)
?~ ship ~
=* term i.t.path
`[u.ship term]
--
--

View File

@ -1,33 +1,33 @@
/+ *group-json
/+ *group-store
=, dejs:format
|_ act=group-action
|_ act=action
++ grab
|%
++ noun group-action
++ json
|= jon=^json
=< (parse-group-action jon)
|%
++ parse-group-action
%- of
:~
[%add add-action]
[%remove remove-action]
[%bundle pa]
[%unbundle pa]
==
::
++ add-action
%- ot
:~ [%members (as (su ;~(pfix sig fed:ag)))]
[%path pa]
==
::
++ remove-action
%- ot
:~ [%members (as (su ;~(pfix sig fed:ag)))]
[%path pa]
==
--
++ noun action
:: ++ json
:: |= jon=^json
:: =< (parse-group-action jon)
:: |%
:: ++ parse-group-action
:: %- of
:: :~
:: [%add add-action]
:: [%remove remove-action]
:: [%bundle pa]
:: [%unbundle pa]
:: ==
:: ::
:: ++ add-action
:: %- ot
:: :~ [%members (as (su ;~(pfix sig fed:ag)))]
:: [%path pa]
:: ==
:: ::
:: ++ remove-action
:: %- ot
:: :~ [%members (as (su ;~(pfix sig fed:ag)))]
:: [%path pa]
:: ==
:: --
--
--

View File

@ -1,9 +1,9 @@
/- *group-hook
=, dejs:format
|_ act=group-hook-action
|_ act=action
++ grab
|%
++ noun group-hook-action
++ noun action
++ json
|= jon=^json
=< (parse-action jon)

View File

@ -1,66 +1,62 @@
/+ *group-json
|_ upd=group-update
/- *group-store
|_ upd=update
++ grab
|%
++ noun group-update
--
++ grow
|%
++ json
=, enjs:format
^- ^json
%+ frond %group-update
%- pairs
:~
?: =(%initial -.upd)
?> ?=(%initial -.upd)
:- %initial
(groups-to-json groups.upd)
::
:: %add
?: =(%add -.upd)
?> ?=(%add -.upd)
:- %add
%- pairs
:~ [%members (set-to-array members.upd ship)]
[%path (path pax.upd)]
==
::
:: %remove
?: =(%remove -.upd)
?> ?=(%remove -.upd)
:- %remove
%- pairs
:~ [%members (set-to-array members.upd ship)]
[%path (path pax.upd)]
==
::
:: %bundle
?: =(%bundle -.upd)
?> ?=(%bundle -.upd)
[%bundle (pairs [%path (path pax.upd)]~)]
::
:: %unbundle
?: =(%unbundle -.upd)
?> ?=(%unbundle -.upd)
[%unbundle (pairs [%path (path pax.upd)]~)]
::
:: %keys
?: =(%keys -.upd)
?> ?=(%keys -.upd)
[%keys (pairs [%keys (set-to-array keys.upd path)]~)]
::
:: %path
?: =(%path -.upd)
?> ?=(%path -.upd)
:- %path
%- pairs
:~ [%members (set-to-array members.upd ship)]
[%path (path pax.upd)]
==
::
:: %noop
[*@t *^json]
==
++ noun update
--
:: ++ grow
:: |%
:: ++ json
:: =, enjs:format
:: ^- ^json
:: %+ frond %group-update
:: %- pairs
:: :~
:: ::
:: :: %add
:: ?: =(%add -.upd)
:: ?> ?=(%add -.upd)
:: :- %add
:: %- pairs
:: :~ [%members (set-to-array members.upd ship)]
:: [%path (path pax.upd)]
:: ==
:: ::
:: :: %remove
:: ?: =(%remove -.upd)
:: ?> ?=(%remove -.upd)
:: :- %remove
:: %- pairs
:: :~ [%members (set-to-array members.upd ship)]
:: [%path (path pax.upd)]
:: ==
:: ::
:: :: %bundle
:: ?: =(%bundle -.upd)
:: ?> ?=(%bundle -.upd)
:: [%bundle (pairs [%path (path pax.upd)]~)]
:: ::
:: :: %unbundle
:: ?: =(%unbundle -.upd)
:: ?> ?=(%unbundle -.upd)
:: [%unbundle (pairs [%path (path pax.upd)]~)]
:: ::
:: :: %keys
:: ?: =(%keys -.upd)
:: ?> ?=(%keys -.upd)
:: [%keys (pairs [%keys (set-to-array keys.upd path)]~)]
:: ::
:: :: %path
:: ?: =(%path -.upd)
:: ?> ?=(%path -.upd)
:: :- %path
:: %- pairs
:: :~ [%members (set-to-array members.upd ship)]
:: [%path (path pax.upd)]
:: ==
:: ::
:: :: %noop
:: [*@t *^json]
:: ==
:: --
--

View File

@ -1,12 +1,26 @@
/- *group, store=group-store
|%
+$ group-hook-action
$% [%add =ship =path] :: if ship is our, make the group publicly
:: available for other ships to sync
:: if ship is foreign, delete any local
:: group at that path and mirror the
:: foreign group at our local path
::
[%remove =path] :: remove the path.
:: $action: request to change group-hook state
::
:: %add:
:: if ship is ours make group available to sync, else sync foreign group
:: to group-store.
:: %remove:
:: if ship is ours make unavailable to sync, else stop syncing foreign
:: group.
::
+$ action
$% [%add =group-id]
[%remove =group-id]
==
:: $update: description of state change
::
:: %no-perms:
:: Group is unavailable to sync
::
+$ update
$% [%no-perms =group-id]
action
==
--
--

View File

@ -1,20 +1,37 @@
/- *group
|%
+$ group (set ship)
:: $action: request to change group-store state
::
+$ group-action
$% [%add members=group pax=path] :: add member to group
[%remove members=group pax=path] :: remove member from group
[%bundle pax=path] :: create group at path
[%unbundle pax=path] :: delete group at path
:: %add-group: add a group
:: %add-members: add members to a group
:: %remove-members: remove members from a group
:: %add-tag: add a tag to a set of ships, creating the tag if it doesn't exist
:: %remove-tag:
:: remove a tag from a set of ships. If the set is empty remove the tag
:: from the group.
:: %change-policy: change a group's policy
:: %remove-group: remove a group from the store
::
+$ action
$% [%add-group =group-id ships=(set ship) =tag-queries =policy]
[%add-members =group-id ships=(set ship) tags=(set term)]
[%remove-members =group-id ships=(set ship)]
[%add-tag =group-id =term ships=(set ship)]
[%remove-tag =group-id =term ships=(set ship)]
[%change-policy =group-id =diff:policy]
[%remove-group =group-id ~]
==
:: $update: a description of a processed state change
::
+$ group-update
$% [%initial =groups]
[%keys keys=(set path)] :: keys have changed
[%path members=group pax=path]
group-action
:: %initial: describe groups upon new subscription
::
+$ update
$% initial
action
==
+$ initial
$% [%initial-group =group-id =group]
[%initial =groups]
==
::
+$ groups (map path group)
--

95
pkg/arvo/sur/group.hoon Normal file
View File

@ -0,0 +1,95 @@
|%
:: $group-id: unique identifier for a group
::
+$ group-id [=ship =term]
:: $groups: a mapping from group-ids to groups
::
+$ groups (map group-id group)
:: $tag: an identifier used to identify a subset of members
::
:: Tags may be used and recognised differently across apps.
:: TODO: document 'blessed' tags
:: example tags:
:: %pending - denotes that member has been invited but not joined yet
:: %admin - denotes that member can add and remove members
::
+$ tag term
:: $permission-tag: a kind of $tag that identifies a privileged user
::
:: These roles are
:: %admin: Administrator, can do everything except delete the group
:: %moderator: Moderator, can add/remove/ban users
:: %janitor: Has no special meaning inside group-store,
:: but may be given additional privileges in other apps.
+$ permission-tag
?(%admin %moderator %janitor)
:: $tag-queries: a mapping from a $tag to the members it identifies
::
+$ tag-queries (jug tag ship)
:: $group: description of a group of users
::
:: members: members of the group
:: tag-queries: a map of subsets
:: policy: permissions for the group
::
+$ group
$: members=(set ship)
=tag-queries
=policy
==
:: $rank: ship class by length
::
:: 0: galaxy or star -- 2 bytes
:: 1: planet -- 4 bytes
:: 2: moon -- 8 bytes
:: 3: comet -- 16 bytes
+$ rank ?(%0 %1 %2 %3)
:: $policy: access control for a group
::
++ policy
=< policy
|%
::
+$ policy
$% invite
open
==
:: $diff: change group policy
+$ diff
$% diff:invite
diff:open
[%replace =policy]
==
:: $invite: allow only invited ships
++ invite
=< invite-policy
|%
::
+$ invite-policy
[%invite pending=(set ship)]
:: $diff: add or remove invites
::
+$ diff
$% [%add-invites invitees=(set ship)]
[%remove-invites invitees=(set ship)]
==
--
:: $open: allow all unbanned ships of approriate rank
::
++ open
=< open-policy
|%
::
+$ open-policy
[%open ranks=(set rank) banned=(set ship)]
:: $diff: ban or allow ranks and ships
::
+$ diff
$% [%allow-ranks ranks=(set rank)]
[%ban-ranks ranks=(set rank)]
[%ban-ships ships=(set ship)]
[%allow-ships ships=(set ship)]
==
--
--
--

View File

@ -0,0 +1,63 @@
/- spider
/+ *ph-io
=>
|%
++ wait-for-agent-start
|= [=ship agent=term]
=/ m (strand:spider ,~)
^- form:m
=* loop $
;< [her=^ship =unix-effect] bind:m take-unix-effect
?: (is-dojo-output:util ship her unix-effect "{(trip agent)} started")
(pure:m ~)
loop
::
++ start-agent
|= [=ship agent=term]
=/ m (strand:spider ,~)
^- form:m
=* loop $
;< ~ bind:m (dojo ship "|start {<agent>}")
;< ~ bind:m (wait-for-agent-start ship agent)
(pure:m ~)
::
++ wait-for-goad
|= =ship
=/ m (strand:spider ,~)
^- form:m
=* loop $
;< [her=^ship =unix-effect] bind:m take-unix-effect
?: (is-dojo-output:util ship her unix-effect "p=%hood q=%bump")
(pure:m ~)
loop
::
++ start-group-agents
|= =ship
=/ m (strand:spider ,~)
^- form:m
;< ~ bind:m (start-agent ship %group-store)
;< ~ bind:m (start-agent ship %group-listen-hook)
;< ~ bind:m (start-agent ship %group-proxy-hook)
(pure:m ~)
--
=, strand=strand:spider
^- thread:spider
|= args=vase
=/ m (strand ,vase)
;< az=tid:spider
bind:m start-azimuth
;< ~ bind:m (spawn az ~bud)
;< ~ bind:m (spawn az ~zod)
;< ~ bind:m (real-ship az ~bud)
;< ~ bind:m (wait-for-goad ~bud)
;< ~ bind:m (real-ship az ~zod)
;< ~ bind:m (wait-for-goad ~zod)
;< ~ bind:m (start-group-agents ~bud)
;< ~ bind:m (start-group-agents ~zod)
;< ~ bind:m (dojo ~bud ":group-store|create 'test-group'")
;< ~ bind:m (wait-for-output ~bud ">=")
;< ~ bind:m (dojo ~zod ":group-store|add ~bud 'test-group'")
;< ~ bind:m (wait-for-output ~zod ">=")
;< ~ bind:m (dojo ~zod ":group-listen-hook|add ~bud 'test-group'")
;< ~ bind:m (wait-for-output ~zod ">=")
(pure:m *vase)