C2S: Implement offerDepC, allowing to create ticket dependencies
This commit is contained in:
parent
90086f1329
commit
a06d273107
5 changed files with 518 additions and 179 deletions
|
@ -19,6 +19,7 @@ module Vervis.API
|
||||||
, createTicketC
|
, createTicketC
|
||||||
, followC
|
, followC
|
||||||
, offerTicketC
|
, offerTicketC
|
||||||
|
, offerDepC
|
||||||
, undoC
|
, undoC
|
||||||
, pushCommitsC
|
, pushCommitsC
|
||||||
, getFollowersCollection
|
, getFollowersCollection
|
||||||
|
@ -114,6 +115,7 @@ import Vervis.RemoteActorStore
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Patch
|
import Vervis.Patch
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
import Vervis.WorkItem
|
||||||
|
|
||||||
parseComment :: LocalURI -> ExceptT Text Handler (ShrIdent, LocalMessageId)
|
parseComment :: LocalURI -> ExceptT Text Handler (ShrIdent, LocalMessageId)
|
||||||
parseComment luParent = do
|
parseComment luParent = do
|
||||||
|
@ -1152,6 +1154,267 @@ offerTicketC (Entity pidUser personUser) sharerUser summary audience ticket uTar
|
||||||
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
|
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
|
||||||
return (doc, makeRecipientSet actors collections)
|
return (doc, makeRecipientSet actors collections)
|
||||||
|
|
||||||
|
offerDepC
|
||||||
|
:: Entity Person
|
||||||
|
-> Sharer
|
||||||
|
-> Maybe TextHtml
|
||||||
|
-> Audience URIMode
|
||||||
|
-> TicketDependency URIMode
|
||||||
|
-> FedURI
|
||||||
|
-> ExceptT Text Handler OutboxItemId
|
||||||
|
offerDepC (Entity pidUser personUser) sharerUser summary audience dep uTarget = do
|
||||||
|
let shrUser = sharerIdent sharerUser
|
||||||
|
(parent, child) <- checkDepAndTarget dep uTarget
|
||||||
|
ParsedAudience localRecips remoteRecips blinded fwdHosts <- do
|
||||||
|
mrecips <- parseAudience audience
|
||||||
|
fromMaybeE mrecips "Offer Ticket with no recipients"
|
||||||
|
federation <- asksSite $ appFederation . appSettings
|
||||||
|
unless (federation || null remoteRecips) $
|
||||||
|
throwE "Federation disabled, but remote recipients specified"
|
||||||
|
verifyHosterRecip localRecips "Parent" parent
|
||||||
|
verifyHosterRecip localRecips "Child" child
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
parentDetail <- runWorkerExcept $ getWorkItemDetail "Parent" parent
|
||||||
|
childDetail <- runWorkerExcept $ getWorkItemDetail "Child" child
|
||||||
|
(obiidOffer, docOffer, remotesHttpOffer, maybeAccept) <- runDBExcept $ do
|
||||||
|
(obiid, doc, luOffer) <- lift $ insertOfferToOutbox shrUser now (personOutbox personUser) blinded
|
||||||
|
remotesHttpOffer <- do
|
||||||
|
wiFollowers <- askWorkItemFollowers
|
||||||
|
let sieve =
|
||||||
|
let (parentA, parentC) =
|
||||||
|
workItemRecipSieve wiFollowers parentDetail
|
||||||
|
(childA, childC) =
|
||||||
|
workItemRecipSieve wiFollowers childDetail
|
||||||
|
in makeRecipientSet
|
||||||
|
(parentA ++ childA)
|
||||||
|
(LocalPersonCollectionSharerFollowers shrUser :
|
||||||
|
parentC ++ childC
|
||||||
|
)
|
||||||
|
moreRemoteRecips <-
|
||||||
|
lift $
|
||||||
|
deliverLocal'
|
||||||
|
True
|
||||||
|
(LocalActorSharer shrUser)
|
||||||
|
(personInbox personUser)
|
||||||
|
obiid
|
||||||
|
(localRecipSieve sieve False localRecips)
|
||||||
|
unless (federation || null moreRemoteRecips) $
|
||||||
|
throwE "Federation disabled, but recipient collection remote members found"
|
||||||
|
lift $ deliverRemoteDB'' fwdHosts obiid remoteRecips moreRemoteRecips
|
||||||
|
maccept <-
|
||||||
|
case (widIdent parentDetail, widIdent childDetail) of
|
||||||
|
(Right _, Left (wi, ltid)) -> do
|
||||||
|
mhoster <-
|
||||||
|
lift $ runMaybeT $
|
||||||
|
case wi of
|
||||||
|
WorkItemSharerTicket shr _ _ -> do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
personInbox <$>
|
||||||
|
MaybeT (getValBy $ UniquePersonIdent sid)
|
||||||
|
WorkItemProjectTicket shr prj _ -> do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
projectInbox <$>
|
||||||
|
MaybeT (getValBy $ UniqueProject prj sid)
|
||||||
|
WorkItemRepoPatch shr rp _ -> do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
repoInbox <$>
|
||||||
|
MaybeT (getValBy $ UniqueRepo rp sid)
|
||||||
|
ibidHoster <- fromMaybeE mhoster "Child hoster not in DB"
|
||||||
|
ibiid <- do
|
||||||
|
mibil <- lift $ getValBy $ UniqueInboxItemLocal ibidHoster obiid
|
||||||
|
inboxItemLocalItem <$>
|
||||||
|
fromMaybeE mibil "Child hoster didn't receive the Offer to their inbox in DB"
|
||||||
|
lift $ insert_ TicketDependencyOffer
|
||||||
|
{ ticketDependencyOfferOffer = ibiid
|
||||||
|
, ticketDependencyOfferChild = ltid
|
||||||
|
}
|
||||||
|
return Nothing
|
||||||
|
(Right _, Right _) -> return Nothing
|
||||||
|
(Left (wi, ltidParent), _) -> Just <$> do
|
||||||
|
mhoster <-
|
||||||
|
lift $ runMaybeT $
|
||||||
|
case wi of
|
||||||
|
WorkItemSharerTicket shr _ _ -> do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
p <- MaybeT (getValBy $ UniquePersonIdent sid)
|
||||||
|
return (personOutbox p, personInbox p)
|
||||||
|
WorkItemProjectTicket shr prj _ -> do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
j <- MaybeT (getValBy $ UniqueProject prj sid)
|
||||||
|
return (projectOutbox j, projectInbox j)
|
||||||
|
WorkItemRepoPatch shr rp _ -> do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
r <- MaybeT (getValBy $ UniqueRepo rp sid)
|
||||||
|
return (repoOutbox r, repoInbox r)
|
||||||
|
(obidHoster, ibidHoster) <- fromMaybeE mhoster "Parent hoster not in DB"
|
||||||
|
obiidAccept <- lift $ insertEmptyOutboxItem obidHoster now
|
||||||
|
tdid <- lift $ insertDep now pidUser obiid ltidParent (widIdent childDetail) obiidAccept
|
||||||
|
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
|
||||||
|
lift $ insertAccept shrUser wi parentDetail childDetail obiid obiidAccept tdid
|
||||||
|
knownRemoteRecipsAccept <-
|
||||||
|
lift $
|
||||||
|
deliverLocal'
|
||||||
|
False
|
||||||
|
(workItemActor wi)
|
||||||
|
ibidHoster
|
||||||
|
obiidAccept
|
||||||
|
localRecipsAccept
|
||||||
|
lift $ (obiidAccept,docAccept,fwdHostsAccept,) <$> deliverRemoteDB'' fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
|
||||||
|
return (obiid, doc, remotesHttpOffer, maccept)
|
||||||
|
lift $ do
|
||||||
|
forkWorker "offerDepC: async HTTP Offer delivery" $ deliverRemoteHttp' fwdHosts obiidOffer docOffer remotesHttpOffer
|
||||||
|
for_ maybeAccept $ \ (obiidAccept, docAccept, fwdHostsAccept, remotesHttpAccept) ->
|
||||||
|
forkWorker "offerDepC: async HTTP Accept delivery" $ deliverRemoteHttp' fwdHostsAccept obiidAccept docAccept remotesHttpAccept
|
||||||
|
return obiidOffer
|
||||||
|
where
|
||||||
|
runWorkerExcept action = do
|
||||||
|
site <- askSite
|
||||||
|
ExceptT $ liftIO $ runWorker (runExceptT action) site
|
||||||
|
verifyHosterRecip _ _ (Right _) = return ()
|
||||||
|
verifyHosterRecip localRecips name (Left wi) =
|
||||||
|
fromMaybeE (verify wi) $
|
||||||
|
name <> " ticket hoster actor isn't listed as a recipient"
|
||||||
|
where
|
||||||
|
verify (WorkItemSharerTicket shr _ _) = do
|
||||||
|
sharerSet <- lookup shr localRecips
|
||||||
|
guard $ localRecipSharer $ localRecipSharerDirect sharerSet
|
||||||
|
verify (WorkItemProjectTicket shr prj _) = do
|
||||||
|
sharerSet <- lookup shr localRecips
|
||||||
|
projectSet <- lookup prj $ localRecipProjectRelated sharerSet
|
||||||
|
guard $ localRecipProject $ localRecipProjectDirect projectSet
|
||||||
|
verify (WorkItemRepoPatch shr rp _) = do
|
||||||
|
sharerSet <- lookup shr localRecips
|
||||||
|
repoSet <- lookup rp $ localRecipRepoRelated sharerSet
|
||||||
|
guard $ localRecipRepo $ localRecipRepoDirect repoSet
|
||||||
|
insertOfferToOutbox shrUser now obid blinded = do
|
||||||
|
hLocal <- asksSite siteInstanceHost
|
||||||
|
obiid <- insertEmptyOutboxItem obid now
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
obikhid <- encodeKeyHashid obiid
|
||||||
|
let luAct = encodeRouteLocal $ SharerOutboxItemR shrUser obikhid
|
||||||
|
doc = Doc hLocal Activity
|
||||||
|
{ activityId = Just luAct
|
||||||
|
, activityActor = encodeRouteLocal $ SharerR shrUser
|
||||||
|
, activitySummary = summary
|
||||||
|
, activityAudience = blinded
|
||||||
|
, activitySpecific =
|
||||||
|
OfferActivity $ Offer (OfferDep dep) uTarget
|
||||||
|
}
|
||||||
|
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
|
||||||
|
return (obiid, doc, luAct)
|
||||||
|
workItemRecipSieve wiFollowers (WorkItemDetail ident context author) =
|
||||||
|
let authorC =
|
||||||
|
case author of
|
||||||
|
Left shr -> [LocalPersonCollectionSharerFollowers shr]
|
||||||
|
Right _ -> []
|
||||||
|
ticketC =
|
||||||
|
case ident of
|
||||||
|
Left (wi, _) -> [wiFollowers wi]
|
||||||
|
Right _ -> []
|
||||||
|
(contextA, contextC) =
|
||||||
|
case context of
|
||||||
|
Left local ->
|
||||||
|
case local of
|
||||||
|
Left (shr, prj) ->
|
||||||
|
( [LocalActorProject shr prj]
|
||||||
|
, [ LocalPersonCollectionProjectTeam shr prj
|
||||||
|
, LocalPersonCollectionProjectFollowers shr prj
|
||||||
|
]
|
||||||
|
)
|
||||||
|
Right (shr, rp) ->
|
||||||
|
( [LocalActorRepo shr rp]
|
||||||
|
, [ LocalPersonCollectionRepoTeam shr rp
|
||||||
|
, LocalPersonCollectionRepoFollowers shr rp
|
||||||
|
]
|
||||||
|
)
|
||||||
|
Right _ -> ([], [])
|
||||||
|
in (contextA, authorC ++ ticketC ++ contextC)
|
||||||
|
insertDep now pidAuthor obiidOffer ltidParent child obiidAccept = do
|
||||||
|
tdid <- insert LocalTicketDependency
|
||||||
|
{ localTicketDependencyParent = ltidParent
|
||||||
|
, localTicketDependencyCreated = now
|
||||||
|
, localTicketDependencyAccept = obiidAccept
|
||||||
|
}
|
||||||
|
case child of
|
||||||
|
Left (_wi, ltid) -> insert_ TicketDependencyChildLocal
|
||||||
|
{ ticketDependencyChildLocalDep = tdid
|
||||||
|
, ticketDependencyChildLocalChild = ltid
|
||||||
|
}
|
||||||
|
Right (ObjURI h lu, _luFollowers) -> do
|
||||||
|
iid <- either entityKey id <$> insertBy' (Instance h)
|
||||||
|
roid <- either entityKey id <$> insertBy' (RemoteObject iid lu)
|
||||||
|
insert_ TicketDependencyChildRemote
|
||||||
|
{ ticketDependencyChildRemoteDep = tdid
|
||||||
|
, ticketDependencyChildRemoteChild = roid
|
||||||
|
}
|
||||||
|
insert_ TicketDependencyAuthorLocal
|
||||||
|
{ ticketDependencyAuthorLocalDep = tdid
|
||||||
|
, ticketDependencyAuthorLocalAuthor = pidAuthor
|
||||||
|
, ticketDependencyAuthorLocalOpen = obiidOffer
|
||||||
|
}
|
||||||
|
return tdid
|
||||||
|
workItemActor (WorkItemSharerTicket shr _ _) = LocalActorSharer shr
|
||||||
|
workItemActor (WorkItemProjectTicket shr prj _) = LocalActorProject shr prj
|
||||||
|
workItemActor (WorkItemRepoPatch shr rp _) = LocalActorRepo shr rp
|
||||||
|
insertAccept shrUser wiParent (WorkItemDetail _ parentCtx parentAuthor) (WorkItemDetail childId childCtx childAuthor) obiidOffer obiidAccept tdid = do
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
wiFollowers <- askWorkItemFollowers
|
||||||
|
hLocal <- asksSite siteInstanceHost
|
||||||
|
|
||||||
|
obikhidOffer <- encodeKeyHashid obiidOffer
|
||||||
|
obikhidAccept <- encodeKeyHashid obiidAccept
|
||||||
|
tdkhid <- encodeKeyHashid tdid
|
||||||
|
|
||||||
|
let audAuthor =
|
||||||
|
AudLocal
|
||||||
|
[LocalActorSharer shrUser]
|
||||||
|
[LocalPersonCollectionSharerFollowers shrUser]
|
||||||
|
audParentContext = contextAudience parentCtx
|
||||||
|
audChildContext = contextAudience childCtx
|
||||||
|
audParentAuthor = authorAudience parentAuthor
|
||||||
|
audParentFollowers = AudLocal [] [wiFollowers wiParent]
|
||||||
|
audChildAuthor = authorAudience childAuthor
|
||||||
|
audChildFollowers =
|
||||||
|
case childId of
|
||||||
|
Left (wi, _ltid) -> AudLocal [] [wiFollowers wi]
|
||||||
|
Right (ObjURI h _, luFollowers) -> AudRemote h [] [luFollowers]
|
||||||
|
|
||||||
|
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
|
||||||
|
collectAudience $
|
||||||
|
audAuthor :
|
||||||
|
audParentAuthor :
|
||||||
|
audParentFollowers :
|
||||||
|
audChildAuthor :
|
||||||
|
audChildFollowers :
|
||||||
|
audParentContext ++ audChildContext
|
||||||
|
|
||||||
|
actor = workItemActor wiParent
|
||||||
|
recips = map encodeRouteHome audLocal ++ audRemote
|
||||||
|
doc = Doc hLocal Activity
|
||||||
|
{ activityId =
|
||||||
|
Just $ encodeRouteLocal $
|
||||||
|
actorOutboxItem actor obikhidAccept
|
||||||
|
, activityActor = encodeRouteLocal $ renderLocalActor actor
|
||||||
|
, activitySummary = Nothing
|
||||||
|
, activityAudience = Audience recips [] [] [] [] []
|
||||||
|
, activitySpecific = AcceptActivity Accept
|
||||||
|
{ acceptObject =
|
||||||
|
encodeRouteHome $ SharerOutboxItemR shrUser obikhidOffer
|
||||||
|
, acceptResult =
|
||||||
|
Just $ encodeRouteLocal $ TicketDepR tdkhid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
|
||||||
|
return (doc, recipientSet, remoteActors, fwdHosts)
|
||||||
|
where
|
||||||
|
authorAudience (Left shr) = AudLocal [LocalActorSharer shr] []
|
||||||
|
authorAudience (Right (ObjURI h lu)) = AudRemote h [lu] []
|
||||||
|
actorOutboxItem (LocalActorSharer shr) = SharerOutboxItemR shr
|
||||||
|
actorOutboxItem (LocalActorProject shr prj) = ProjectOutboxItemR shr prj
|
||||||
|
actorOutboxItem (LocalActorRepo shr rp) = RepoOutboxItemR shr rp
|
||||||
|
|
||||||
undoC
|
undoC
|
||||||
:: ShrIdent
|
:: ShrIdent
|
||||||
-> Maybe TextHtml
|
-> Maybe TextHtml
|
||||||
|
|
|
@ -85,6 +85,7 @@ import Vervis.Model.Ident
|
||||||
import Vervis.Model.Ticket
|
import Vervis.Model.Ticket
|
||||||
import Vervis.Patch
|
import Vervis.Patch
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
import Vervis.WorkItem
|
||||||
|
|
||||||
checkOffer
|
checkOffer
|
||||||
:: AP.Ticket URIMode
|
:: AP.Ticket URIMode
|
||||||
|
@ -954,147 +955,6 @@ sharerOfferDepF now shrRecip author body mfwd luOffer dep uTarget = do
|
||||||
else LocalPersonCollectionSharerTicketFollowers
|
else LocalPersonCollectionSharerTicketFollowers
|
||||||
in coll shrRecip (hashTALID talid)
|
in coll shrRecip (hashTALID talid)
|
||||||
|
|
||||||
data WorkItemDetail = WorkItemDetail
|
|
||||||
{ widIdent :: Either (WorkItem, LocalTicketId) (FedURI, LocalURI)
|
|
||||||
, widContext :: Either (Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent)) (FedURI, Host, Maybe LocalURI, Maybe LocalURI)
|
|
||||||
, widAuthor :: Either ShrIdent FedURI
|
|
||||||
}
|
|
||||||
|
|
||||||
getAuthor
|
|
||||||
:: MonadIO m
|
|
||||||
=> Either
|
|
||||||
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
|
||||||
(Entity TicketAuthorRemote)
|
|
||||||
-> ReaderT SqlBackend m (Either ShrIdent (Instance, RemoteObject))
|
|
||||||
getAuthor =
|
|
||||||
bitraverse
|
|
||||||
(\ (Entity _ tal, _) -> do
|
|
||||||
p <- getJust $ ticketAuthorLocalAuthor tal
|
|
||||||
sharerIdent <$> getJust (personIdent p)
|
|
||||||
)
|
|
||||||
(\ (Entity _ tar) -> do
|
|
||||||
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
|
||||||
ro <- getJust $ remoteActorIdent ra
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (i, ro)
|
|
||||||
)
|
|
||||||
|
|
||||||
getWorkItemDetail
|
|
||||||
:: Text -> Either WorkItem FedURI -> ExceptT Text Worker WorkItemDetail
|
|
||||||
getWorkItemDetail name v = do
|
|
||||||
manager <- asksSite appHttpManager
|
|
||||||
(childId, childCtx, childAuthor) <-
|
|
||||||
case v of
|
|
||||||
Left wi -> runSiteDBExcept $ do
|
|
||||||
(ltid, ctx, author) <- getWorkItem name wi
|
|
||||||
return (Left (wi, ltid), second mkuri ctx, second mkuri author)
|
|
||||||
Right u -> do
|
|
||||||
Doc hAuthor t <- withExceptT T.pack $ AP.fetchAP manager $ Left u
|
|
||||||
(hTicket, tl) <- fromMaybeE (AP.ticketLocal t) $ name <> ": no 'id'"
|
|
||||||
unless (ObjURI hAuthor (AP.ticketId tl) == u) $
|
|
||||||
throwE "Ticket 'id' differs from the URI we fetched"
|
|
||||||
uCtx <- fromMaybeE (AP.ticketContext t) "Ticket without 'context'"
|
|
||||||
ctx <- parseTicketContext uCtx
|
|
||||||
author <- parseTicketAuthor $ ObjURI hTicket (AP.ticketAttributedTo t)
|
|
||||||
return (Right (u, AP.ticketParticipants tl), ctx, author)
|
|
||||||
childCtx' <- bifor childCtx pure $ \ u -> do
|
|
||||||
obj <- withExceptT T.pack $ AP.fetchAP manager $ Left u
|
|
||||||
unless (objId obj == u) $
|
|
||||||
throwE "Project 'id' differs from the URI we fetched"
|
|
||||||
u' <-
|
|
||||||
case (objContext obj, objInbox obj) of
|
|
||||||
(Just c, Nothing) -> do
|
|
||||||
hl <- hostIsLocal $ objUriAuthority c
|
|
||||||
when hl $ throwE $ name <> ": remote context has a local context"
|
|
||||||
pure c
|
|
||||||
(Nothing, Just _) -> pure u
|
|
||||||
_ -> throwE "Umm context-inbox thing"
|
|
||||||
return
|
|
||||||
(u', objUriAuthority u, objFollowers obj, objTeam obj)
|
|
||||||
return $ WorkItemDetail childId childCtx' childAuthor
|
|
||||||
where
|
|
||||||
getWorkItem name (WorkItemSharerTicket shr talid False) = do
|
|
||||||
(_, Entity ltid _, _, context) <- do
|
|
||||||
mticket <- lift $ getSharerTicket shr talid
|
|
||||||
fromMaybeE mticket $ name <> ": No such sharer-ticket"
|
|
||||||
context' <-
|
|
||||||
lift $
|
|
||||||
bitraverse
|
|
||||||
(\ (_, Entity _ tpl) -> do
|
|
||||||
j <- getJust $ ticketProjectLocalProject tpl
|
|
||||||
s <- getJust $ projectSharer j
|
|
||||||
return $ Left (sharerIdent s, projectIdent j)
|
|
||||||
)
|
|
||||||
(\ (Entity _ tcr, _) -> do
|
|
||||||
roid <-
|
|
||||||
case ticketProjectRemoteProject tcr of
|
|
||||||
Nothing ->
|
|
||||||
remoteActorIdent <$>
|
|
||||||
getJust (ticketProjectRemoteTracker tcr)
|
|
||||||
Just roid -> return roid
|
|
||||||
ro <- getJust roid
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (i, ro)
|
|
||||||
)
|
|
||||||
context
|
|
||||||
return (ltid, context', Left shr)
|
|
||||||
getWorkItem name (WorkItemSharerTicket shr talid True) = do
|
|
||||||
(_, Entity ltid _, _, context, _) <- do
|
|
||||||
mticket <- lift $ getSharerPatch shr talid
|
|
||||||
fromMaybeE mticket $ name <> ": No such sharer-patch"
|
|
||||||
context' <-
|
|
||||||
lift $
|
|
||||||
bitraverse
|
|
||||||
(\ (_, Entity _ trl) -> do
|
|
||||||
r <- getJust $ ticketRepoLocalRepo trl
|
|
||||||
s <- getJust $ repoSharer r
|
|
||||||
return $ Right (sharerIdent s, repoIdent r)
|
|
||||||
)
|
|
||||||
(\ (Entity _ tcr, _) -> do
|
|
||||||
roid <-
|
|
||||||
case ticketProjectRemoteProject tcr of
|
|
||||||
Nothing ->
|
|
||||||
remoteActorIdent <$>
|
|
||||||
getJust (ticketProjectRemoteTracker tcr)
|
|
||||||
Just roid -> return roid
|
|
||||||
ro <- getJust roid
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (i, ro)
|
|
||||||
)
|
|
||||||
context
|
|
||||||
return (ltid, context', Left shr)
|
|
||||||
getWorkItem name (WorkItemProjectTicket shr prj ltid) = do
|
|
||||||
mticket <- lift $ getProjectTicket shr prj ltid
|
|
||||||
(Entity _ s, Entity _ j, _, _, _, _, author) <-
|
|
||||||
fromMaybeE mticket $ name <> ": No such project-ticket"
|
|
||||||
author' <- lift $ getAuthor author
|
|
||||||
return (ltid, Left $ Left (sharerIdent s, projectIdent j), author')
|
|
||||||
getWorkItem name (WorkItemRepoPatch shr rp ltid) = do
|
|
||||||
mticket <- lift $ getRepoPatch shr rp ltid
|
|
||||||
(Entity _ s, Entity _ r, _, _, _, _, author, _) <-
|
|
||||||
fromMaybeE mticket $ name <> ": No such repo-patch"
|
|
||||||
author' <- lift $ getAuthor author
|
|
||||||
return (ltid, Left $ Right (sharerIdent s, repoIdent r), author')
|
|
||||||
parseTicketContext u@(ObjURI h lu) = do
|
|
||||||
hl <- hostIsLocal h
|
|
||||||
if hl
|
|
||||||
then Left <$> do
|
|
||||||
route <- fromMaybeE (decodeRouteLocal lu) "Not a route"
|
|
||||||
case route of
|
|
||||||
ProjectR shr prj -> return $ Left (shr, prj)
|
|
||||||
RepoR shr rp -> return $ Right (shr, rp)
|
|
||||||
_ -> throwE "Not a ticket context route"
|
|
||||||
else return $ Right u
|
|
||||||
parseTicketAuthor u@(ObjURI h lu) = do
|
|
||||||
hl <- hostIsLocal h
|
|
||||||
if hl
|
|
||||||
then Left <$> do
|
|
||||||
route <- fromMaybeE (decodeRouteLocal lu) "Not a route"
|
|
||||||
case route of
|
|
||||||
SharerR shr -> return shr
|
|
||||||
_ -> throwE "Not a ticket author route"
|
|
||||||
else return $ Right u
|
|
||||||
|
|
||||||
mkuri (i, ro) = ObjURI (instanceHost i) (remoteObjectIdent ro)
|
mkuri (i, ro) = ObjURI (instanceHost i) (remoteObjectIdent ro)
|
||||||
|
|
||||||
insertDep
|
insertDep
|
||||||
|
@ -1131,42 +991,6 @@ insertDep now author ractidOffer ltidParent child obiidAccept = do
|
||||||
}
|
}
|
||||||
return tdid
|
return tdid
|
||||||
|
|
||||||
askWorkItemFollowers
|
|
||||||
:: (MonadSite m, YesodHashids (SiteEnv m))
|
|
||||||
=> m (WorkItem -> LocalPersonCollection)
|
|
||||||
askWorkItemFollowers = do
|
|
||||||
hashTALID <- getEncodeKeyHashid
|
|
||||||
hashLTID <- getEncodeKeyHashid
|
|
||||||
let workItemFollowers (WorkItemSharerTicket shr talid False) = LocalPersonCollectionSharerTicketFollowers shr $ hashTALID talid
|
|
||||||
workItemFollowers (WorkItemSharerTicket shr talid True) = LocalPersonCollectionSharerPatchFollowers shr $ hashTALID talid
|
|
||||||
workItemFollowers (WorkItemProjectTicket shr prj ltid) = LocalPersonCollectionProjectTicketFollowers shr prj $ hashLTID ltid
|
|
||||||
workItemFollowers (WorkItemRepoPatch shr rp ltid) = LocalPersonCollectionRepoPatchFollowers shr rp $ hashLTID ltid
|
|
||||||
return workItemFollowers
|
|
||||||
|
|
||||||
contextAudience
|
|
||||||
:: Either
|
|
||||||
(Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent))
|
|
||||||
(FedURI, Host, Maybe LocalURI, Maybe LocalURI)
|
|
||||||
-> [Aud URIMode]
|
|
||||||
contextAudience ctx =
|
|
||||||
case ctx of
|
|
||||||
Left (Left (shr, prj)) ->
|
|
||||||
pure $ AudLocal
|
|
||||||
[LocalActorProject shr prj]
|
|
||||||
[ LocalPersonCollectionProjectTeam shr prj
|
|
||||||
, LocalPersonCollectionProjectFollowers shr prj
|
|
||||||
]
|
|
||||||
Left (Right (shr, rp)) ->
|
|
||||||
pure $ AudLocal
|
|
||||||
[LocalActorRepo shr rp]
|
|
||||||
[ LocalPersonCollectionRepoTeam shr rp
|
|
||||||
, LocalPersonCollectionRepoFollowers shr rp
|
|
||||||
]
|
|
||||||
Right (ObjURI hTracker luTracker, hProject, luFollowers, luTeam) ->
|
|
||||||
[ AudRemote hTracker [luTracker] []
|
|
||||||
, AudRemote hProject [] (catMaybes [luFollowers, luTeam])
|
|
||||||
]
|
|
||||||
|
|
||||||
projectOfferDepF
|
projectOfferDepF
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
-> ShrIdent
|
-> ShrIdent
|
||||||
|
@ -1190,7 +1014,7 @@ projectOfferDepF now shrRecip prjRecip author body mfwd luOffer dep uTarget = do
|
||||||
(_, _, _, _, _, _, author) <- do
|
(_, _, _, _, _, _, author) <- do
|
||||||
mticket <- lift $ getProjectTicket shrRecip prjRecip parentLtid
|
mticket <- lift $ getProjectTicket shrRecip prjRecip parentLtid
|
||||||
fromMaybeE mticket $ "Parent" <> ": No such project-ticket"
|
fromMaybeE mticket $ "Parent" <> ": No such project-ticket"
|
||||||
lift $ getAuthor author
|
lift $ getWorkItemAuthorDetail author
|
||||||
childDetail <- getWorkItemDetail "Child" child
|
childDetail <- getWorkItemDetail "Child" child
|
||||||
return (parentLtid, parentAuthor, childDetail)
|
return (parentLtid, parentAuthor, childDetail)
|
||||||
mhttp <- runSiteDBExcept $ do
|
mhttp <- runSiteDBExcept $ do
|
||||||
|
@ -1351,7 +1175,7 @@ repoOfferDepF now shrRecip rpRecip author body mfwd luOffer dep uTarget = do
|
||||||
(_, _, _, _, _, _, author, _) <- do
|
(_, _, _, _, _, _, author, _) <- do
|
||||||
mticket <- lift $ getRepoPatch shrRecip rpRecip parentLtid
|
mticket <- lift $ getRepoPatch shrRecip rpRecip parentLtid
|
||||||
fromMaybeE mticket $ "Parent" <> ": No such repo-patch"
|
fromMaybeE mticket $ "Parent" <> ": No such repo-patch"
|
||||||
lift $ getAuthor author
|
lift $ getWorkItemAuthorDetail author
|
||||||
childDetail <- getWorkItemDetail "Child" child
|
childDetail <- getWorkItemDetail "Child" child
|
||||||
return (parentLtid, parentAuthor, childDetail)
|
return (parentLtid, parentAuthor, childDetail)
|
||||||
mhttp <- runSiteDBExcept $ do
|
mhttp <- runSiteDBExcept $ do
|
||||||
|
|
|
@ -302,6 +302,8 @@ postSharerOutboxR shr = do
|
||||||
case obj of
|
case obj of
|
||||||
OfferTicket ticket ->
|
OfferTicket ticket ->
|
||||||
offerTicketC eperson sharer summary audience ticket target
|
offerTicketC eperson sharer summary audience ticket target
|
||||||
|
OfferDep dep ->
|
||||||
|
offerDepC eperson sharer summary audience dep target
|
||||||
_ -> throwE "Unsupported Offer 'object' type"
|
_ -> throwE "Unsupported Offer 'object' type"
|
||||||
UndoActivity undo ->
|
UndoActivity undo ->
|
||||||
undoC shr summary audience undo
|
undoC shr summary audience undo
|
||||||
|
|
249
src/Vervis/WorkItem.hs
Normal file
249
src/Vervis/WorkItem.hs
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
-
|
||||||
|
- The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
- rights to this software to the public domain worldwide. This software is
|
||||||
|
- distributed without any warranty.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
- with this software. If not, see
|
||||||
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Vervis.WorkItem
|
||||||
|
( WorkItemDetail (..)
|
||||||
|
, getWorkItemAuthorDetail
|
||||||
|
, askWorkItemFollowers
|
||||||
|
, contextAudience
|
||||||
|
, getWorkItemDetail
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Control.Monad.Trans.Class
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
|
-- import Control.Monad.Trans.Maybe
|
||||||
|
import Control.Monad.Trans.Reader
|
||||||
|
import Data.Bifunctor
|
||||||
|
import Data.Bitraversable
|
||||||
|
-- import Data.Either
|
||||||
|
-- import Data.Foldable (for_)
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Text (Text)
|
||||||
|
-- import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Database.Persist.Sql
|
||||||
|
-- import Yesod.Core (notFound)
|
||||||
|
-- import Yesod.Core.Content
|
||||||
|
-- import Yesod.Persist.Core
|
||||||
|
|
||||||
|
-- import qualified Database.Esqueleto as E
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
import Network.FedURI
|
||||||
|
import Web.ActivityPub
|
||||||
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
|
import Yesod.MonadSite
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Control.Monad.Trans.Except.Local
|
||||||
|
-- import Data.Either.Local
|
||||||
|
-- import Data.Paginate.Local
|
||||||
|
-- import Database.Persist.Local
|
||||||
|
-- import Yesod.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.ActivityPub.Recipient
|
||||||
|
import Vervis.FedURI
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
-- import Vervis.Model.Workflow
|
||||||
|
-- import Vervis.Paginate
|
||||||
|
import Vervis.Patch
|
||||||
|
import Vervis.Ticket
|
||||||
|
-- import Vervis.Widget.Ticket (TicketSummary (..))
|
||||||
|
|
||||||
|
data WorkItemDetail = WorkItemDetail
|
||||||
|
{ widIdent :: Either (WorkItem, LocalTicketId) (FedURI, LocalURI)
|
||||||
|
, widContext :: Either (Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent)) (FedURI, Host, Maybe LocalURI, Maybe LocalURI)
|
||||||
|
, widAuthor :: Either ShrIdent FedURI
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorkItemAuthorDetail
|
||||||
|
:: MonadIO m
|
||||||
|
=> Either
|
||||||
|
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
||||||
|
(Entity TicketAuthorRemote)
|
||||||
|
-> ReaderT SqlBackend m (Either ShrIdent (Instance, RemoteObject))
|
||||||
|
getWorkItemAuthorDetail =
|
||||||
|
bitraverse
|
||||||
|
(\ (Entity _ tal, _) -> do
|
||||||
|
p <- getJust $ ticketAuthorLocalAuthor tal
|
||||||
|
sharerIdent <$> getJust (personIdent p)
|
||||||
|
)
|
||||||
|
(\ (Entity _ tar) -> do
|
||||||
|
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
||||||
|
ro <- getJust $ remoteActorIdent ra
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
|
||||||
|
askWorkItemFollowers
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> m (WorkItem -> LocalPersonCollection)
|
||||||
|
askWorkItemFollowers = do
|
||||||
|
hashTALID <- getEncodeKeyHashid
|
||||||
|
hashLTID <- getEncodeKeyHashid
|
||||||
|
let workItemFollowers (WorkItemSharerTicket shr talid False) = LocalPersonCollectionSharerTicketFollowers shr $ hashTALID talid
|
||||||
|
workItemFollowers (WorkItemSharerTicket shr talid True) = LocalPersonCollectionSharerPatchFollowers shr $ hashTALID talid
|
||||||
|
workItemFollowers (WorkItemProjectTicket shr prj ltid) = LocalPersonCollectionProjectTicketFollowers shr prj $ hashLTID ltid
|
||||||
|
workItemFollowers (WorkItemRepoPatch shr rp ltid) = LocalPersonCollectionRepoPatchFollowers shr rp $ hashLTID ltid
|
||||||
|
return workItemFollowers
|
||||||
|
|
||||||
|
contextAudience
|
||||||
|
:: Either
|
||||||
|
(Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent))
|
||||||
|
(FedURI, Host, Maybe LocalURI, Maybe LocalURI)
|
||||||
|
-> [Aud URIMode]
|
||||||
|
contextAudience ctx =
|
||||||
|
case ctx of
|
||||||
|
Left (Left (shr, prj)) ->
|
||||||
|
pure $ AudLocal
|
||||||
|
[LocalActorProject shr prj]
|
||||||
|
[ LocalPersonCollectionProjectTeam shr prj
|
||||||
|
, LocalPersonCollectionProjectFollowers shr prj
|
||||||
|
]
|
||||||
|
Left (Right (shr, rp)) ->
|
||||||
|
pure $ AudLocal
|
||||||
|
[LocalActorRepo shr rp]
|
||||||
|
[ LocalPersonCollectionRepoTeam shr rp
|
||||||
|
, LocalPersonCollectionRepoFollowers shr rp
|
||||||
|
]
|
||||||
|
Right (ObjURI hTracker luTracker, hProject, luFollowers, luTeam) ->
|
||||||
|
[ AudRemote hTracker [luTracker] []
|
||||||
|
, AudRemote hProject [] (catMaybes [luFollowers, luTeam])
|
||||||
|
]
|
||||||
|
|
||||||
|
getWorkItemDetail
|
||||||
|
:: Text -> Either WorkItem FedURI -> ExceptT Text Worker WorkItemDetail
|
||||||
|
getWorkItemDetail name v = do
|
||||||
|
manager <- asksSite appHttpManager
|
||||||
|
(childId, childCtx, childAuthor) <-
|
||||||
|
case v of
|
||||||
|
Left wi -> runSiteDBExcept $ do
|
||||||
|
(ltid, ctx, author) <- getWorkItem name wi
|
||||||
|
return (Left (wi, ltid), second mkuri ctx, second mkuri author)
|
||||||
|
Right u -> do
|
||||||
|
Doc hAuthor t <- withExceptT T.pack $ AP.fetchAP manager $ Left u
|
||||||
|
(hTicket, tl) <- fromMaybeE (AP.ticketLocal t) $ name <> ": no 'id'"
|
||||||
|
unless (ObjURI hAuthor (AP.ticketId tl) == u) $
|
||||||
|
throwE "Ticket 'id' differs from the URI we fetched"
|
||||||
|
uCtx <- fromMaybeE (AP.ticketContext t) "Ticket without 'context'"
|
||||||
|
ctx <- parseTicketContext uCtx
|
||||||
|
author <- parseTicketAuthor $ ObjURI hTicket (AP.ticketAttributedTo t)
|
||||||
|
return (Right (u, AP.ticketParticipants tl), ctx, author)
|
||||||
|
childCtx' <- bifor childCtx pure $ \ u -> do
|
||||||
|
obj <- withExceptT T.pack $ AP.fetchAP manager $ Left u
|
||||||
|
unless (objId obj == u) $
|
||||||
|
throwE "Project 'id' differs from the URI we fetched"
|
||||||
|
u' <-
|
||||||
|
case (objContext obj, objInbox obj) of
|
||||||
|
(Just c, Nothing) -> do
|
||||||
|
hl <- hostIsLocal $ objUriAuthority c
|
||||||
|
when hl $ throwE $ name <> ": remote context has a local context"
|
||||||
|
pure c
|
||||||
|
(Nothing, Just _) -> pure u
|
||||||
|
_ -> throwE "Umm context-inbox thing"
|
||||||
|
return
|
||||||
|
(u', objUriAuthority u, objFollowers obj, objTeam obj)
|
||||||
|
return $ WorkItemDetail childId childCtx' childAuthor
|
||||||
|
where
|
||||||
|
getWorkItem name (WorkItemSharerTicket shr talid False) = do
|
||||||
|
(_, Entity ltid _, _, context) <- do
|
||||||
|
mticket <- lift $ getSharerTicket shr talid
|
||||||
|
fromMaybeE mticket $ name <> ": No such sharer-ticket"
|
||||||
|
context' <-
|
||||||
|
lift $
|
||||||
|
bitraverse
|
||||||
|
(\ (_, Entity _ tpl) -> do
|
||||||
|
j <- getJust $ ticketProjectLocalProject tpl
|
||||||
|
s <- getJust $ projectSharer j
|
||||||
|
return $ Left (sharerIdent s, projectIdent j)
|
||||||
|
)
|
||||||
|
(\ (Entity _ tcr, _) -> do
|
||||||
|
roid <-
|
||||||
|
case ticketProjectRemoteProject tcr of
|
||||||
|
Nothing ->
|
||||||
|
remoteActorIdent <$>
|
||||||
|
getJust (ticketProjectRemoteTracker tcr)
|
||||||
|
Just roid -> return roid
|
||||||
|
ro <- getJust roid
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
context
|
||||||
|
return (ltid, context', Left shr)
|
||||||
|
getWorkItem name (WorkItemSharerTicket shr talid True) = do
|
||||||
|
(_, Entity ltid _, _, context, _) <- do
|
||||||
|
mticket <- lift $ getSharerPatch shr talid
|
||||||
|
fromMaybeE mticket $ name <> ": No such sharer-patch"
|
||||||
|
context' <-
|
||||||
|
lift $
|
||||||
|
bitraverse
|
||||||
|
(\ (_, Entity _ trl) -> do
|
||||||
|
r <- getJust $ ticketRepoLocalRepo trl
|
||||||
|
s <- getJust $ repoSharer r
|
||||||
|
return $ Right (sharerIdent s, repoIdent r)
|
||||||
|
)
|
||||||
|
(\ (Entity _ tcr, _) -> do
|
||||||
|
roid <-
|
||||||
|
case ticketProjectRemoteProject tcr of
|
||||||
|
Nothing ->
|
||||||
|
remoteActorIdent <$>
|
||||||
|
getJust (ticketProjectRemoteTracker tcr)
|
||||||
|
Just roid -> return roid
|
||||||
|
ro <- getJust roid
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
context
|
||||||
|
return (ltid, context', Left shr)
|
||||||
|
getWorkItem name (WorkItemProjectTicket shr prj ltid) = do
|
||||||
|
mticket <- lift $ getProjectTicket shr prj ltid
|
||||||
|
(Entity _ s, Entity _ j, _, _, _, _, author) <-
|
||||||
|
fromMaybeE mticket $ name <> ": No such project-ticket"
|
||||||
|
author' <- lift $ getWorkItemAuthorDetail author
|
||||||
|
return (ltid, Left $ Left (sharerIdent s, projectIdent j), author')
|
||||||
|
getWorkItem name (WorkItemRepoPatch shr rp ltid) = do
|
||||||
|
mticket <- lift $ getRepoPatch shr rp ltid
|
||||||
|
(Entity _ s, Entity _ r, _, _, _, _, author, _) <-
|
||||||
|
fromMaybeE mticket $ name <> ": No such repo-patch"
|
||||||
|
author' <- lift $ getWorkItemAuthorDetail author
|
||||||
|
return (ltid, Left $ Right (sharerIdent s, repoIdent r), author')
|
||||||
|
parseTicketContext u@(ObjURI h lu) = do
|
||||||
|
hl <- hostIsLocal h
|
||||||
|
if hl
|
||||||
|
then Left <$> do
|
||||||
|
route <- fromMaybeE (decodeRouteLocal lu) "Not a route"
|
||||||
|
case route of
|
||||||
|
ProjectR shr prj -> return $ Left (shr, prj)
|
||||||
|
RepoR shr rp -> return $ Right (shr, rp)
|
||||||
|
_ -> throwE "Not a ticket context route"
|
||||||
|
else return $ Right u
|
||||||
|
parseTicketAuthor u@(ObjURI h lu) = do
|
||||||
|
hl <- hostIsLocal h
|
||||||
|
if hl
|
||||||
|
then Left <$> do
|
||||||
|
route <- fromMaybeE (decodeRouteLocal lu) "Not a route"
|
||||||
|
case route of
|
||||||
|
SharerR shr -> return shr
|
||||||
|
_ -> throwE "Not a ticket author route"
|
||||||
|
else return $ Right u
|
||||||
|
mkuri (i, ro) = ObjURI (instanceHost i) (remoteObjectIdent ro)
|
|
@ -216,6 +216,7 @@ library
|
||||||
Vervis.Widget.Ticket
|
Vervis.Widget.Ticket
|
||||||
Vervis.Widget.Workflow
|
Vervis.Widget.Workflow
|
||||||
Vervis.Wiki
|
Vervis.Wiki
|
||||||
|
Vervis.WorkItem
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
default-extensions: TemplateHaskell
|
default-extensions: TemplateHaskell
|
||||||
QuasiQuotes
|
QuasiQuotes
|
||||||
|
|
Loading…
Reference in a new issue