C2S: Implement team creation
This commit is contained in:
parent
2797e5f3be
commit
ea7476db9d
6 changed files with 252 additions and 2 deletions
47
migrations/549_2023-11-21_group_create.model
Normal file
47
migrations/549_2023-11-21_group_create.model
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
Inbox
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
justCreatedBy ActorId Maybe
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Group
|
||||||
|
actor ActorId
|
||||||
|
create OutboxItemId
|
||||||
|
|
||||||
|
UniqueGroupActor actor
|
||||||
|
UniqueGroupCreate create
|
||||||
|
|
||||||
|
Person
|
||||||
|
username Username
|
||||||
|
login Text
|
||||||
|
passphraseHash ByteString
|
||||||
|
email EmailAddress
|
||||||
|
verified Bool
|
||||||
|
verifiedKey Text
|
||||||
|
verifiedKeyCreated UTCTime
|
||||||
|
resetPassKey Text
|
||||||
|
resetPassKeyCreated UTCTime
|
||||||
|
actor ActorId
|
||||||
|
-- reviewFollow Bool
|
||||||
|
|
||||||
|
UniquePersonUsername username
|
||||||
|
UniquePersonLogin login
|
||||||
|
UniquePersonEmail email
|
||||||
|
UniquePersonActor actor
|
|
@ -61,6 +61,7 @@ import Vervis.ActivityPub
|
||||||
import Vervis.Actor
|
import Vervis.Actor
|
||||||
import Vervis.Actor2
|
import Vervis.Actor2
|
||||||
import Vervis.Actor.Deck
|
import Vervis.Actor.Deck
|
||||||
|
import Vervis.Actor.Group
|
||||||
import Vervis.Actor.Project
|
import Vervis.Actor.Project
|
||||||
import Vervis.Cloth
|
import Vervis.Cloth
|
||||||
import Vervis.Data.Actor
|
import Vervis.Data.Actor
|
||||||
|
@ -620,6 +621,163 @@ clientCreateProject now personMeID (ClientMsg maybeCap localRecips remoteRecips
|
||||||
}
|
}
|
||||||
return (action, recipientSet, remoteActors, fwdHosts)
|
return (action, recipientSet, remoteActors, fwdHosts)
|
||||||
|
|
||||||
|
-- Meaning: The human wants to create a team
|
||||||
|
-- Behavior:
|
||||||
|
-- * Create a team on DB
|
||||||
|
-- * Launch a team actor
|
||||||
|
-- * Record a FollowRequest in DB
|
||||||
|
-- * Create and send Create and Follow to it
|
||||||
|
clientCreateTeam
|
||||||
|
:: UTCTime
|
||||||
|
-> PersonId
|
||||||
|
-> ClientMsg
|
||||||
|
-> AP.ActorDetail
|
||||||
|
-> ActE OutboxItemId
|
||||||
|
clientCreateTeam now personMeID (ClientMsg maybeCap localRecips remoteRecips fwdHosts action) tracker = do
|
||||||
|
|
||||||
|
-- Check input
|
||||||
|
verifyNothingE maybeCap "Capability not needed"
|
||||||
|
(name, msummary) <- parseTracker tracker
|
||||||
|
|
||||||
|
(actorMeID, localRecipsFinal, createID, actionCreate, followID, follow, groupID) <- lift $ withDB $ do
|
||||||
|
|
||||||
|
-- Grab me from DB
|
||||||
|
(personMe, actorMe) <- do
|
||||||
|
p <- getJust personMeID
|
||||||
|
(p,) <$> getJust (personActor p)
|
||||||
|
let actorMeID = personActor personMe
|
||||||
|
|
||||||
|
-- Insert new team to DB
|
||||||
|
createID <- insertEmptyOutboxItem' (actorOutbox actorMe) now
|
||||||
|
(groupID, projectFollowerSetID) <-
|
||||||
|
insertTeam now name msummary createID actorMeID
|
||||||
|
|
||||||
|
-- Insert the Create activity to my outbox
|
||||||
|
groupHash <- lift $ encodeKeyHashid groupID
|
||||||
|
actionCreate <- lift $ prepareCreate name msummary groupHash
|
||||||
|
luCreate <- updateOutboxItem' (LocalActorPerson personMeID) createID actionCreate
|
||||||
|
|
||||||
|
-- Prepare recipient sieve for sending the Create
|
||||||
|
personMeHash <- lift $ encodeKeyHashid personMeID
|
||||||
|
let sieve =
|
||||||
|
makeRecipientSet
|
||||||
|
[LocalActorGroup groupHash]
|
||||||
|
[LocalStagePersonFollowers personMeHash]
|
||||||
|
onlyGroup = GroupRoutes True False
|
||||||
|
addMe' groups = (groupHash, onlyGroup) : groups
|
||||||
|
addMe rs = rs { recipGroups = addMe' $ recipGroups rs }
|
||||||
|
|
||||||
|
-- Insert a follow request, since I'm about to send a Follow
|
||||||
|
followID <- insertEmptyOutboxItem' (actorOutbox actorMe) now
|
||||||
|
insert_ $ FollowRequest actorMeID projectFollowerSetID True followID
|
||||||
|
|
||||||
|
-- Insert a Follow to my outbox
|
||||||
|
follow@(actionFollow, _, _, _) <- lift $ prepareFollow groupID luCreate
|
||||||
|
_luFollow <- updateOutboxItem' (LocalActorPerson personMeID) followID actionFollow
|
||||||
|
|
||||||
|
return
|
||||||
|
( personActor personMe
|
||||||
|
, localRecipSieve sieve False $ addMe localRecips
|
||||||
|
, createID
|
||||||
|
, actionCreate
|
||||||
|
, followID
|
||||||
|
, follow
|
||||||
|
, groupID
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Spawn new Group actor
|
||||||
|
success <- lift $ launchActor LocalActorGroup groupID
|
||||||
|
unless success $
|
||||||
|
error "Failed to spawn new Group, somehow ID already in Theater"
|
||||||
|
|
||||||
|
-- Send the Create
|
||||||
|
lift $ sendActivity
|
||||||
|
(LocalActorPerson personMeID) actorMeID localRecipsFinal remoteRecips
|
||||||
|
fwdHosts createID actionCreate
|
||||||
|
|
||||||
|
-- Send the Follow
|
||||||
|
let (actionFollow, localRecipsFollow, remoteRecipsFollow, fwdHostsFollow) = follow
|
||||||
|
lift $ sendActivity
|
||||||
|
(LocalActorPerson personMeID) actorMeID localRecipsFollow
|
||||||
|
remoteRecipsFollow fwdHostsFollow followID actionFollow
|
||||||
|
|
||||||
|
return createID
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
parseTracker (AP.ActorDetail typ muser mname msummary) = do
|
||||||
|
unless (typ == AP.ActorTypeTeam) $
|
||||||
|
error "clientCreateTeam: Create object isn't a Team"
|
||||||
|
verifyNothingE muser "Team can't have a username"
|
||||||
|
name <- fromMaybeE mname "Team doesn't specify name"
|
||||||
|
return (name, msummary)
|
||||||
|
|
||||||
|
insertTeam now name msummary obiidCreate actorMeID = do
|
||||||
|
ibid <- insert Inbox
|
||||||
|
obid <- insert Outbox
|
||||||
|
fsid <- insert FollowerSet
|
||||||
|
aid <- insert Actor
|
||||||
|
{ actorName = name
|
||||||
|
, actorDesc = fromMaybe "" msummary
|
||||||
|
, actorCreatedAt = now
|
||||||
|
, actorInbox = ibid
|
||||||
|
, actorOutbox = obid
|
||||||
|
, actorFollowers = fsid
|
||||||
|
, actorJustCreatedBy = Just actorMeID
|
||||||
|
}
|
||||||
|
gid <- insert Group
|
||||||
|
{ groupActor = aid
|
||||||
|
, groupCreate = obiidCreate
|
||||||
|
}
|
||||||
|
return (gid, fsid)
|
||||||
|
|
||||||
|
prepareCreate name msummary groupHash = do
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
hLocal <- asksEnv stageInstanceHost
|
||||||
|
let ttdetail = AP.ActorDetail
|
||||||
|
{ AP.actorType = AP.ActorTypeTeam
|
||||||
|
, AP.actorUsername = Nothing
|
||||||
|
, AP.actorName = Just name
|
||||||
|
, AP.actorSummary = msummary
|
||||||
|
}
|
||||||
|
ttlocal = AP.ActorLocal
|
||||||
|
{ AP.actorId = encodeRouteLocal $ GroupR groupHash
|
||||||
|
, AP.actorInbox = encodeRouteLocal $ GroupInboxR groupHash
|
||||||
|
, AP.actorOutbox = Nothing
|
||||||
|
, AP.actorFollowers = Nothing
|
||||||
|
, AP.actorFollowing = Nothing
|
||||||
|
, AP.actorPublicKeys = []
|
||||||
|
, AP.actorSshKeys = []
|
||||||
|
}
|
||||||
|
specific = AP.CreateActivity AP.Create
|
||||||
|
{ AP.createObject = AP.CreateTeam ttdetail (Just (hLocal, ttlocal))
|
||||||
|
, AP.createTarget = Nothing
|
||||||
|
}
|
||||||
|
return action { AP.actionSpecific = specific }
|
||||||
|
|
||||||
|
prepareFollow groupID luCreate = do
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
h <- asksEnv stageInstanceHost
|
||||||
|
groupHash <- encodeKeyHashid groupID
|
||||||
|
|
||||||
|
let audTopic = AudLocal [LocalActorGroup groupHash] []
|
||||||
|
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
|
||||||
|
collectAudience [audTopic]
|
||||||
|
|
||||||
|
recips = map encodeRouteHome audLocal ++ audRemote
|
||||||
|
action = AP.Action
|
||||||
|
{ AP.actionCapability = Nothing
|
||||||
|
, AP.actionSummary = Nothing
|
||||||
|
, AP.actionAudience = AP.Audience recips [] [] [] [] []
|
||||||
|
, AP.actionFulfills = [ObjURI h luCreate]
|
||||||
|
, AP.actionSpecific = AP.FollowActivity AP.Follow
|
||||||
|
{ AP.followObject = encodeRouteHome $ GroupR groupHash
|
||||||
|
, AP.followContext = Nothing
|
||||||
|
, AP.followHide = False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (action, recipientSet, remoteActors, fwdHosts)
|
||||||
|
|
||||||
clientCreate
|
clientCreate
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
-> PersonId
|
-> PersonId
|
||||||
|
@ -639,6 +797,11 @@ clientCreate now personMeID msg (AP.Create object muTarget) =
|
||||||
verifyNothingE muTarget "'target' not supported in Create Project"
|
verifyNothingE muTarget "'target' not supported in Create Project"
|
||||||
clientCreateProject now personMeID msg detail
|
clientCreateProject now personMeID msg detail
|
||||||
|
|
||||||
|
AP.CreateTeam detail mlocal -> do
|
||||||
|
verifyNothingE mlocal "Team id must not be provided"
|
||||||
|
verifyNothingE muTarget "'target' not supported in Create Team"
|
||||||
|
clientCreateTeam now personMeID msg detail
|
||||||
|
|
||||||
_ -> throwE "Unsupported Create object for C2S"
|
_ -> throwE "Unsupported Create object for C2S"
|
||||||
|
|
||||||
-- Meaning: The human wants to invite someone A to a resource R
|
-- Meaning: The human wants to invite someone A to a resource R
|
||||||
|
|
|
@ -3021,6 +3021,32 @@ changes hLocal ctx =
|
||||||
"OutboxItem"
|
"OutboxItem"
|
||||||
-- 548
|
-- 548
|
||||||
, addUnique' "CollabFulfillsInvite" "Accept" ["accept"]
|
, addUnique' "CollabFulfillsInvite" "Accept" ["accept"]
|
||||||
|
-- 549
|
||||||
|
, addFieldRefRequired''
|
||||||
|
"Group"
|
||||||
|
(do obid <- insert Outbox549
|
||||||
|
let doc = persistJSONObjectFromDoc $ Doc hLocal emptyActivity
|
||||||
|
insertEntity $ OutboxItem549 obid doc defaultTime
|
||||||
|
)
|
||||||
|
(Just $ \ (Entity obiidTemp obiTemp) -> do
|
||||||
|
let doc = persistJSONObjectFromDoc $ Doc hLocal emptyActivity
|
||||||
|
gs <- selectKeysList ([] :: [Filter Group549]) []
|
||||||
|
for_ gs $ \ gid -> do
|
||||||
|
obid <- do
|
||||||
|
mp <- selectFirst [] [Asc Person549Id]
|
||||||
|
p <- entityVal <$> maybe (error "No people") return mp
|
||||||
|
a <- getJust $ person549Actor p
|
||||||
|
return $ actor549Outbox a
|
||||||
|
obiid <- insert $ OutboxItem549 obid doc defaultTime
|
||||||
|
update gid [Group549Create =. obiid]
|
||||||
|
|
||||||
|
delete obiidTemp
|
||||||
|
delete $ outboxItem549Outbox obiTemp
|
||||||
|
)
|
||||||
|
"create"
|
||||||
|
"OutboxItem"
|
||||||
|
-- 550
|
||||||
|
, addUnique' "Group" "Create" ["create"]
|
||||||
]
|
]
|
||||||
|
|
||||||
migrateDB
|
migrateDB
|
||||||
|
|
|
@ -528,3 +528,6 @@ makeEntitiesMigration "527"
|
||||||
|
|
||||||
makeEntitiesMigration "547"
|
makeEntitiesMigration "547"
|
||||||
$(modelFile "migrations/547_2023-06-28_invite_accept.model")
|
$(modelFile "migrations/547_2023-06-28_invite_accept.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "549"
|
||||||
|
$(modelFile "migrations/549_2023-11-21_group_create.model")
|
||||||
|
|
|
@ -1831,6 +1831,7 @@ data CreateObject u
|
||||||
| CreateRepository ActorDetail VersionControlSystem (Maybe (Authority u, ActorLocal u))
|
| CreateRepository ActorDetail VersionControlSystem (Maybe (Authority u, ActorLocal u))
|
||||||
| CreatePatchTracker ActorDetail (NonEmpty (ObjURI u)) (Maybe (Authority u, ActorLocal u))
|
| CreatePatchTracker ActorDetail (NonEmpty (ObjURI u)) (Maybe (Authority u, ActorLocal u))
|
||||||
| CreateProject ActorDetail (Maybe (Authority u, ActorLocal u))
|
| CreateProject ActorDetail (Maybe (Authority u, ActorLocal u))
|
||||||
|
| CreateTeam ActorDetail (Maybe (Authority u, ActorLocal u))
|
||||||
|
|
||||||
parseCreateObject :: UriMode u => Object -> Parser (CreateObject u)
|
parseCreateObject :: UriMode u => Object -> Parser (CreateObject u)
|
||||||
parseCreateObject o
|
parseCreateObject o
|
||||||
|
@ -1858,6 +1859,11 @@ parseCreateObject o
|
||||||
fail "type isn't Project"
|
fail "type isn't Project"
|
||||||
ml <- parseActorLocal o
|
ml <- parseActorLocal o
|
||||||
return $ CreateProject d ml
|
return $ CreateProject d ml
|
||||||
|
<|> do d <- parseActorDetail o
|
||||||
|
unless (actorType d == ActorTypeTeam) $
|
||||||
|
fail "type isn't Team"
|
||||||
|
ml <- parseActorLocal o
|
||||||
|
return $ CreateTeam d ml
|
||||||
|
|
||||||
encodeCreateObject :: UriMode u => CreateObject u -> Series
|
encodeCreateObject :: UriMode u => CreateObject u -> Series
|
||||||
encodeCreateObject (CreateNote h note) = toSeries h note
|
encodeCreateObject (CreateNote h note) = toSeries h note
|
||||||
|
@ -1874,6 +1880,8 @@ encodeCreateObject (CreatePatchTracker d repos ml)
|
||||||
<> maybe mempty (uncurry encodeActorLocal) ml
|
<> maybe mempty (uncurry encodeActorLocal) ml
|
||||||
encodeCreateObject (CreateProject d ml) =
|
encodeCreateObject (CreateProject d ml) =
|
||||||
encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml
|
encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml
|
||||||
|
encodeCreateObject (CreateTeam d ml) =
|
||||||
|
encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml
|
||||||
|
|
||||||
data Create u = Create
|
data Create u = Create
|
||||||
{ createObject :: CreateObject u
|
{ createObject :: CreateObject u
|
||||||
|
@ -1894,6 +1902,7 @@ parseCreate o a luActor = do
|
||||||
CreateRepository _ _ _ -> return ()
|
CreateRepository _ _ _ -> return ()
|
||||||
CreatePatchTracker _ _ _ -> return ()
|
CreatePatchTracker _ _ _ -> return ()
|
||||||
CreateProject _ _ -> return ()
|
CreateProject _ _ -> return ()
|
||||||
|
CreateTeam _ _ -> return ()
|
||||||
Create obj <$> o .:? "target"
|
Create obj <$> o .:? "target"
|
||||||
|
|
||||||
encodeCreate :: UriMode u => Create u -> Series
|
encodeCreate :: UriMode u => Create u -> Series
|
||||||
|
|
|
@ -270,9 +270,11 @@ SshKey
|
||||||
UniqueSshKey person ident
|
UniqueSshKey person ident
|
||||||
|
|
||||||
Group
|
Group
|
||||||
actor ActorId
|
actor ActorId
|
||||||
|
create OutboxItemId
|
||||||
|
|
||||||
UniqueGroupActor actor
|
UniqueGroupActor actor
|
||||||
|
UniqueGroupCreate create
|
||||||
|
|
||||||
GroupMember
|
GroupMember
|
||||||
person PersonId
|
person PersonId
|
||||||
|
|
Loading…
Reference in a new issue