fradrive/src/Jobs/Handler/SynchroniseAvs.hs

105 lines
4.7 KiB
Haskell

-- SPDX-FileCopyrightText: 2022-23 Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
module Jobs.Handler.SynchroniseAvs
( dispatchJobSynchroniseAvs
, dispatchJobSynchroniseAvsId
, dispatchJobSynchroniseAvsUser
, dispatchJobSynchroniseAvsNext
, dispatchJobSynchroniseAvsQueue
) where
import Import
import qualified Database.Esqueleto.Legacy as E hiding (upsert)
import qualified Database.Esqueleto.PostgreSQL as E
import qualified Database.Esqueleto.Utils as E
import qualified Data.Conduit.List as C
import Jobs.Queue
import Handler.Utils.Avs
dispatchJobSynchroniseAvs :: Natural -> Natural -> Natural -> Maybe Day -> JobHandler UniWorX
dispatchJobSynchroniseAvs numIterations epoch iteration pause
-- TODO: refactor so that the AvsIdLookup becomes obsolete
= JobHandlerAtomic . runConduit $
readUsers .| filterIteration .| sinkDBJobs
where
readUsers :: ConduitT () UserId (YesodJobDB UniWorX) ()
readUsers = selectKeys [] []
filterIteration :: ConduitT UserId Job (YesodJobDB UniWorX) ()
filterIteration = C.mapMaybeM $ \userId -> runMaybeT $ do
let
userIteration, currentIteration :: Integer
userIteration = toInteger (hash epoch `hashWithSalt` userId) `mod` toInteger numIterations
currentIteration = toInteger iteration `mod` toInteger numIterations
-- $logDebugS "SynchronisAvs" [st|User ##{tshow (fromSqlKey userId)}: sync on #{tshow userIteration}/#{tshow numIterations}, now #{tshow currentIteration}|]
guard $ userIteration == currentIteration
return $ JobSynchroniseAvsUser userId pause
-- dispatchJobSynchroniseAvs' :: Natural -> Natural -> Natural -> Maybe Day -> JobHandler UniWorX
-- dispatchJobSynchroniseAvs' numIterations epoch iteration pause = JobHandlerAtomic $ do
dispatchJobSynchroniseAvsId :: AvsPersonId -> Maybe Day -> JobHandler UniWorX
dispatchJobSynchroniseAvsId apid pause = JobHandlerException $ do
ok <- runDBJobs $
getBy (UniqueUserAvsId apid) >>= \case
(Just Entity{entityVal=UserAvs{userAvsUser=uid}}) -> do -- known user
workJobSychronizeAvs uid pause
return True
Nothing -> -- unknown avsPersonId, attempt to create user
return False
-- flip (maybeM $ return False) (getBy $ UniqueUserAvsId apid) $ \Entity{entityVal=UserAvs{userAvsUser=uid}} -> do -- known user
-- workJobSychronizeAvs uid pause
-- return True
unless ok $ void $ maybeCatchAll $ upsertAvsUserById apid
dispatchJobSynchroniseAvsUser :: UserId -> Maybe Day -> JobHandler UniWorX
dispatchJobSynchroniseAvsUser uid pause = JobHandlerException $ runDBJobs $ workJobSychronizeAvs uid pause
workJobSychronizeAvs :: UserId -> Maybe Day -> JobDB ()
workJobSychronizeAvs uid pause = do
now <- liftIO getCurrentTime
void $ E.upsert
AvsSync { avsSyncUser = uid
, avsSyncCreationTime = now
, avsSyncPause = pause
}
[ \oldSync -> (AvsSyncPause E.=. E.greatest (E.val pause) (oldSync E.^. AvsSyncPause)) oldSync ]
queueDBJob JobSynchroniseAvsQueue
dispatchJobSynchroniseAvsQueue :: JobHandler UniWorX
dispatchJobSynchroniseAvsQueue = JobHandlerException $ do
syncJob <- runDB $
selectFirst [] [Asc AvsSyncCreationTime] >>= \case
Nothing -> return Nothing -- nothing more to do
Just Entity{entityKey=asid, entityVal=AvsSync{..}} -> do
delete asid
getBy (UniqueUserAvsUser avsSyncUser) >>= \case
Just uae@Entity{entityVal=UserAvs{userAvsLastSynch} }
| maybe True (utctDay userAvsLastSynch <) avsSyncPause -> return $ Just uae
_other -> return Nothing -- we just updated this one within the given limit or the entity does not exist
ifMaybeM syncJob () $ \Entity{entityKey=avsKey, entityVal=UserAvs{userAvsPersonId=apid}} -> do
void $ queueJob JobSynchroniseAvsNext
catch (void $ upsertAvsUserById apid) -- already updates UserAvsLastSynch
(\exc -> do
now <- liftIO getCurrentTime
let excMsg = tshow exc <> " at " <> tshow now
runDB (update avsKey [UserAvsLastSynchError =. Just excMsg, UserAvsLastSynch =. now])
case exc of
AvsInterfaceUnavailable -> return () -- ignore and retry later
AvsUserUnknownByAvs _ -> return () -- ignore for users no longer listed in AVS
otherExc -> throwM otherExc
)
-- needed, since JobSynchroniseAvsQueue cannot requeue itself due to JobNoQueueSame (and having no parameters)
dispatchJobSynchroniseAvsNext :: JobHandler UniWorX
dispatchJobSynchroniseAvsNext = JobHandlerException $ void $ queueJob JobSynchroniseAvsQueue