chore(avs): upsertAvsUser now has a failsafe alternative
This commit is contained in:
parent
21fe05ea95
commit
3d51f2ebdb
@ -25,7 +25,7 @@ UserAvs
|
|||||||
-- Purpose of saving cards is to detect external changes in qualifications and postal addresses
|
-- Purpose of saving cards is to detect external changes in qualifications and postal addresses
|
||||||
UserAvsCard
|
UserAvsCard
|
||||||
personId AvsPersonId
|
personId AvsPersonId
|
||||||
cardNo AvsCardNo
|
cardNo AvsFullCardNo
|
||||||
card AvsDataPersonCard
|
card AvsDataPersonCard
|
||||||
lastSynch UTCTime
|
lastSynch UTCTime
|
||||||
UniqueAvsCard cardNo
|
UniqueAvsCard cardNo
|
||||||
|
|||||||
@ -155,7 +155,7 @@ postAdminAvsR = do
|
|||||||
flip (renderAForm FormStandard) html $ areq textField (fslI MsgAvsCardNo) Nothing
|
flip (renderAForm FormStandard) html $ areq textField (fslI MsgAvsCardNo) Nothing
|
||||||
let procFormCrUsr fr = do
|
let procFormCrUsr fr = do
|
||||||
-- addMessage Info $ text2Html $ "Query: " <> tshow (toJSON fr)
|
-- addMessage Info $ text2Html $ "Query: " <> tshow (toJSON fr)
|
||||||
res <- try $ upsertAvsUser fr
|
res <- try $ guessAvsUser fr
|
||||||
case res of
|
case res of
|
||||||
(Right (Just uid)) -> do
|
(Right (Just uid)) -> do
|
||||||
uuid :: CryptoUUIDUser <- encrypt uid
|
uuid :: CryptoUUIDUser <- encrypt uid
|
||||||
|
|||||||
@ -151,7 +151,7 @@ postCAddUserR tid ssh csh = do
|
|||||||
return $ AddUserRequest <$> auReqUsers <*> auReqTutorial
|
return $ AddUserRequest <$> auReqUsers <*> auReqTutorial
|
||||||
|
|
||||||
formResult usersToAdd $ \AddUserRequest{..} -> do
|
formResult usersToAdd $ \AddUserRequest{..} -> do
|
||||||
avsUsers :: Map UserSearchKey (Maybe UserId) <- sequenceA $ Map.fromSet upsertAvsUser auReqUsers
|
avsUsers :: Map UserSearchKey (Maybe UserId) <- sequenceA $ Map.fromSet guessAvsUser auReqUsers
|
||||||
let (usersFound, usersNotFound) = partition (is _Just . view _2) $ Map.toList avsUsers
|
let (usersFound, usersNotFound) = partition (is _Just . view _2) $ Map.toList avsUsers
|
||||||
unless (null usersNotFound) $
|
unless (null usersNotFound) $
|
||||||
let msgContent = [whamlet|
|
let msgContent = [whamlet|
|
||||||
|
|||||||
@ -344,9 +344,10 @@ postUsersR = do
|
|||||||
(UserRemoveSupervisorData, userSet) -> do
|
(UserRemoveSupervisorData, userSet) -> do
|
||||||
runDB $ deleteWhere [UserSupervisorUser <-. Set.toList userSet]
|
runDB $ deleteWhere [UserSupervisorUser <-. Set.toList userSet]
|
||||||
addMessageI Success $ MsgUsersRemoveSupervisors $ Set.size userSet
|
addMessageI Success $ MsgUsersRemoveSupervisors $ Set.size userSet
|
||||||
|
redirect UsersR
|
||||||
(act, usersSet)
|
(act, usersSet)
|
||||||
| isActionSupervisor act -> do
|
| isActionSupervisor act -> do
|
||||||
avsUsers :: Map Text (Maybe UserId) <- sequenceA $ Map.fromSet upsertAvsUser $ getActionSupervisors act
|
avsUsers :: Map Text (Maybe UserId) <- sequenceA $ Map.fromSet guessAvsUser $ getActionSupervisors act
|
||||||
let (supersFound, supersNotFound) = partition (is _Just . view _2) $ Map.toList avsUsers
|
let (supersFound, supersNotFound) = partition (is _Just . view _2) $ Map.toList avsUsers
|
||||||
users = Set.toList usersSet
|
users = Set.toList usersSet
|
||||||
nrSuperNotFound = length supersNotFound
|
nrSuperNotFound = length supersNotFound
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
module Handler.Utils.Avs
|
module Handler.Utils.Avs
|
||||||
( validQualification, validQualification'
|
( validQualification, validQualification'
|
||||||
|
, guessAvsUser
|
||||||
, upsertAvsUser, upsertAvsUserById, upsertAvsUserByCard
|
, upsertAvsUser, upsertAvsUserById, upsertAvsUserByCard
|
||||||
-- , getLicence, getLicenceDB, getLicenceByAvsId -- not supported by interface
|
-- , getLicence, getLicenceDB, getLicenceByAvsId -- not supported by interface
|
||||||
, AvsLicenceDifferences(..)
|
, AvsLicenceDifferences(..)
|
||||||
@ -24,15 +25,15 @@ import Import
|
|||||||
-- import qualified Database.Esqueleto.Legacy as E
|
-- import qualified Database.Esqueleto.Legacy as E
|
||||||
|
|
||||||
import Utils.Avs
|
import Utils.Avs
|
||||||
|
import Utils.Users
|
||||||
|
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
-- import Auth.LDAP (ldapUserPrincipalName)
|
-- import Auth.LDAP (ldapUserPrincipalName)
|
||||||
import Foundation.Yesod.Auth (ldapLookupAndUpsert) -- , CampusUserConversionException())
|
import Foundation.Yesod.Auth (ldapLookupAndUpsert) -- , CampusUserConversionException())
|
||||||
|
|
||||||
import Utils.Users
|
|
||||||
import Handler.Utils.Company
|
import Handler.Utils.Company
|
||||||
|
|
||||||
import Database.Esqueleto.Experimental ((:&)(..))
|
import Database.Esqueleto.Experimental ((:&)(..))
|
||||||
@ -288,13 +289,50 @@ getDifferingLicences (AvsResponseGetLicences licences) = do
|
|||||||
set to 2: rollfeld-set && nicht in vorfeld-unset && (in vorfeld-set || AVS_Licence>0 == vorORrollfeld)
|
set to 2: rollfeld-set && nicht in vorfeld-unset && (in vorfeld-set || AVS_Licence>0 == vorORrollfeld)
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
-- | Find or upsert User by AvsCardId (with dot), Fraport PersonalNumber, Fraport Email-Address or by prefixed AvsId or prefixed AvsNo; fail-safe, may or may not update existing users, may insert new users
|
||||||
|
-- If an existing User with internal number is found, an AVS query is executed
|
||||||
|
guessAvsUser :: Text -> Handler (Maybe UserId)
|
||||||
|
guessAvsUser (Text.splitAt 6 -> ("AVSID:", avsidTxt)) = ifMaybeM (readMay avsidTxt) Nothing $ \avsidNr ->
|
||||||
|
let avsid = AvsPersonId avsidNr
|
||||||
|
maybeAvsUpsert = maybeCatchAll $ upsertAvsUserById avsid
|
||||||
|
extractUid (Entity _ UserAvs{userAvsUser=uid}) = return $ Just uid
|
||||||
|
in maybeM maybeAvsUpsert extractUid $ runDB $ getBy $ UniqueUserAvsId avsid
|
||||||
|
guessAvsUser (Text.splitAt 6 -> ("AVSNO:", avsnoTxt)) = ifMaybeM (readMay avsnoTxt) Nothing $ \avsno ->
|
||||||
|
runDB (selectList [UserAvsNoPerson ==. avsno] []) >>= \case
|
||||||
|
[Entity _ UserAvs{userAvsUser=uid}] -> return $ Just uid
|
||||||
|
_ -> return Nothing
|
||||||
|
guessAvsUser someid = do
|
||||||
|
let maybeUpsertAvsUserByCard = maybeCatchAll . upsertAvsUserByCard
|
||||||
|
extractUid (Entity _ UserAvs{userAvsUser=uid}) = return $ Just uid
|
||||||
|
extractUidCard (Entity _ UserAvsCard{userAvsCardPersonId=avid}) = getBy $ UniqueUserAvsId avid
|
||||||
|
case discernAvsCardPersonalNo someid of
|
||||||
|
Just cid@(Left cardNo) ->
|
||||||
|
maybeM (maybeUpsertAvsUserByCard cid) extractUid $ runDB $
|
||||||
|
maybeM (return Nothing) extractUidCard $ getBy $ UniqueAvsCard cardNo
|
||||||
|
Just cid@(Right _wholeNumber) ->
|
||||||
|
maybeUpsertAvsUserByCard cid >>= \case
|
||||||
|
Nothing ->
|
||||||
|
runDB (selectList [UserCompanyPersonalNumber ==. Just someid] []) >>= \case
|
||||||
|
[Entity uid _] -> return $ Just uid
|
||||||
|
_ -> return Nothing
|
||||||
|
uid -> return uid
|
||||||
|
Nothing -> try (runDB $ ldapLookupAndUpsert someid) >>= \case
|
||||||
|
Right Entity{entityKey=uid, entityVal=User{userCompanyPersonalNumber=Just persNo}} ->
|
||||||
|
maybeM (return $ Just uid) (return . Just) (maybeUpsertAvsUserByCard (Right $ mkAvsInternalPersonalNo persNo))
|
||||||
|
Right Entity{entityKey=uid} -> return $ Just uid
|
||||||
|
other -> do -- attempt to recover by trying other ids
|
||||||
|
whenIsLeft other (\(err::SomeException) -> $logInfoS "AVS" $ "upsertAvsUser LDAP error " <> tshow err) -- this line primarily forces exception type to catch-all
|
||||||
|
runDB . runMaybeT $
|
||||||
|
let someIdent = stripCI someid
|
||||||
|
in MaybeT (getKeyBy $ UniqueEmail someIdent)
|
||||||
|
<|> MaybeT (getKeyBy $ UniqueAuthentication someIdent)
|
||||||
|
|
||||||
-- | Always update AVS Data, accepts AvsCardId (with dot), Fraport PersonalNumber or Fraport Email-Adress
|
-- | Always update AVS Data, accepts AvsCardId (with dot), Fraport PersonalNumber or Fraport Email-Address
|
||||||
upsertAvsUser :: Text -> Handler (Maybe UserId) -- TODO: change to Entity
|
upsertAvsUser :: Text -> Handler (Maybe UserId) -- TODO: change to Entity
|
||||||
upsertAvsUser (discernAvsCardPersonalNo -> Just someid) = upsertAvsUserByCard someid -- Note: Right case is any number; it could be AvsCardNumber or AvsInternalPersonalNumber; we cannot know, but the latter is much more likely and useful to users!
|
upsertAvsUser (discernAvsCardPersonalNo -> Just someid) = maybeCatchAll $ upsertAvsUserByCard someid -- Note: Right case is any number; it could be AvsCardNumber or AvsInternalPersonalNumber; we cannot know, but the latter is much more likely and useful to users!
|
||||||
upsertAvsUser otherId = -- attempt LDAP lookup to find by eMail
|
upsertAvsUser otherId = -- attempt LDAP lookup to find by eMail
|
||||||
try (runDB $ ldapLookupAndUpsert otherId) >>= \case
|
try (runDB $ ldapLookupAndUpsert otherId) >>= \case
|
||||||
Right Entity{entityVal=User{userCompanyPersonalNumber=Just persNo}} -> upsertAvsUserByCard (Right $ mkAvsInternalPersonalNo persNo)
|
Right Entity{entityVal=User{userCompanyPersonalNumber=Just persNo}} -> maybeCatchAll $ upsertAvsUserByCard (Right $ mkAvsInternalPersonalNo persNo)
|
||||||
other -> do -- attempt to recover by trying other ids
|
other -> do -- attempt to recover by trying other ids
|
||||||
whenIsLeft other (\(err::SomeException) -> $logInfoS "AVS" $ "upsertAvsUser LDAP error " <> tshow err) -- this line primarily forces exception type to catch-all
|
whenIsLeft other (\(err::SomeException) -> $logInfoS "AVS" $ "upsertAvsUser LDAP error " <> tshow err) -- this line primarily forces exception type to catch-all
|
||||||
apid <- runDB . runMaybeT $ do
|
apid <- runDB . runMaybeT $ do
|
||||||
@ -389,7 +427,7 @@ upsertAvsUserById api = do
|
|||||||
forM_ avsPersonPersonCards $ -- save all cards for later
|
forM_ avsPersonPersonCards $ -- save all cards for later
|
||||||
-- let cs :: Set AvsDataPersonCard = Set.fromList $ catMaybes [pinCard, addrCard]
|
-- let cs :: Set AvsDataPersonCard = Set.fromList $ catMaybes [pinCard, addrCard]
|
||||||
-- forM_ cs $ -- only save used cards for the postal address update detection
|
-- forM_ cs $ -- only save used cards for the postal address update detection
|
||||||
\avsCard -> insert_ $ UserAvsCard avsPersonPersonID (avsDataCardNo avsCard) avsCard now
|
\avsCard -> insert_ $ UserAvsCard avsPersonPersonID (getFullCardNo avsCard) avsCard now
|
||||||
upsertUserCompany uid mbCompany
|
upsertUserCompany uid mbCompany
|
||||||
return mbUid
|
return mbUid
|
||||||
|
|
||||||
@ -407,15 +445,15 @@ upsertAvsUserById api = do
|
|||||||
unless (maybe True (`Set.member` oldAddrs) mbCoFirmAddr) $ do -- update postal address, unless the exact address had been seen before
|
unless (maybe True (`Set.member` oldAddrs) mbCoFirmAddr) $ do -- update postal address, unless the exact address had been seen before
|
||||||
updateWhere [UserId ==. uid] [UserPostAddress =. userFirmAddr]
|
updateWhere [UserId ==. uid] [UserPostAddress =. userFirmAddr]
|
||||||
whenIsJust pinCard $ \pCard -> -- update pin, but only if it was unset or set to the value of an old card
|
whenIsJust pinCard $ \pCard -> -- update pin, but only if it was unset or set to the value of an old card
|
||||||
unlessM (exists [UserAvsCardCardNo ==. avsDataCardNo pCard]) $ do
|
unlessM (exists [UserAvsCardCardNo ==. getFullCardNo pCard]) $ do
|
||||||
let oldPins = Just . personCard2pin . userAvsCardCard . entityVal <$> oldCards
|
let oldPins = Just . personCard2pin . userAvsCardCard . entityVal <$> oldCards
|
||||||
updateWhere [UserId ==. uid, UserPinPassword !=. userPin, UserPinPassword <-. Nothing:oldPins]
|
updateWhere [UserId ==. uid, UserPinPassword !=. userPin, UserPinPassword <-. Nothing:oldPins]
|
||||||
[UserPinPassword =. userPin]
|
[UserPinPassword =. userPin]
|
||||||
insert_ $ UserAvsCard api (avsDataCardNo pCard) pCard now
|
insert_ $ UserAvsCard api (getFullCardNo pCard) pCard now
|
||||||
upsertUserCompany uid mbCompany
|
upsertUserCompany uid mbCompany
|
||||||
forM_ avsPersonPersonCards $ \aCard -> void $ upsert UserAvsCard
|
forM_ avsPersonPersonCards $ \aCard -> void $ upsert UserAvsCard
|
||||||
{ userAvsCardPersonId = api
|
{ userAvsCardPersonId = api
|
||||||
, userAvsCardCardNo = avsDataCardNo aCard
|
, userAvsCardCardNo = getFullCardNo aCard
|
||||||
, userAvsCardCard = aCard
|
, userAvsCardCard = aCard
|
||||||
, userAvsCardLastSynch = now
|
, userAvsCardLastSynch = now
|
||||||
}
|
}
|
||||||
|
|||||||
@ -140,8 +140,11 @@ instance {-# OVERLAPS #-} Canonical (Maybe AvsInternalPersonalNo) where
|
|||||||
type AvsVersionNo = Text -- always 1 digit
|
type AvsVersionNo = Text -- always 1 digit
|
||||||
newtype AvsCardNo = AvsCardNo { avsCardNo :: Text } -- always 8 digits
|
newtype AvsCardNo = AvsCardNo { avsCardNo :: Text } -- always 8 digits
|
||||||
deriving (Eq, Ord, Show, Generic)
|
deriving (Eq, Ord, Show, Generic)
|
||||||
deriving newtype (NFData, PathPiece, PersistField, PersistFieldSql, Csv.ToField, Csv.FromField)
|
deriving newtype (NFData, PathPiece, Csv.ToField, Csv.FromField)
|
||||||
instance E.SqlString AvsCardNo
|
-- No longer needed:
|
||||||
|
-- deriving newtype (PersistField, PersistFieldSql)
|
||||||
|
-- instance E.SqlString AvsCardNo
|
||||||
|
|
||||||
-- AvsCardNo is an untagged Text with respect to FromJSON/ToJSON, as needed by AVS API
|
-- AvsCardNo is an untagged Text with respect to FromJSON/ToJSON, as needed by AVS API
|
||||||
instance FromJSON AvsCardNo where
|
instance FromJSON AvsCardNo where
|
||||||
parseJSON x = AvsCardNo . normalizeAvsCardNo <$> parseJSON x
|
parseJSON x = AvsCardNo . normalizeAvsCardNo <$> parseJSON x
|
||||||
@ -156,7 +159,7 @@ instance Canonical AvsCardNo where
|
|||||||
|
|
||||||
|
|
||||||
data AvsFullCardNo = AvsFullCardNo { avsFullCardNo :: AvsCardNo, avsFullCardVersion :: AvsVersionNo }
|
data AvsFullCardNo = AvsFullCardNo { avsFullCardNo :: AvsCardNo, avsFullCardVersion :: AvsVersionNo }
|
||||||
deriving (Eq, Ord, Generic)
|
deriving (Eq, Ord, Generic, NFData)
|
||||||
|
|
||||||
tshowAvsFullCardNo :: AvsFullCardNo -> Text
|
tshowAvsFullCardNo :: AvsFullCardNo -> Text
|
||||||
tshowAvsFullCardNo AvsFullCardNo{..} = avsCardNo avsFullCardNo <> Text.cons '.' avsFullCardVersion
|
tshowAvsFullCardNo AvsFullCardNo{..} = avsCardNo avsFullCardNo <> Text.cons '.' avsFullCardVersion
|
||||||
@ -170,6 +173,16 @@ readAvsFullCardNo (Text.span Char.isDigit -> (c, Text.uncons -> Just ('.',v)))
|
|||||||
= Just $ AvsFullCardNo (AvsCardNo c) v
|
= Just $ AvsFullCardNo (AvsCardNo c) v
|
||||||
readAvsFullCardNo _ = Nothing
|
readAvsFullCardNo _ = Nothing
|
||||||
|
|
||||||
|
instance PersistField AvsFullCardNo where
|
||||||
|
toPersistValue = PersistText . tshowAvsFullCardNo
|
||||||
|
fromPersistValue (PersistText t)
|
||||||
|
| Just afc <- readAvsFullCardNo t = Right afc
|
||||||
|
| otherwise = Left $ "Encoding of AvsFullCardNo is invalid: " <> t
|
||||||
|
fromPersistValue other = Left $ "Encoding of AvsFullCardNo with invalid type: " <> tshow other
|
||||||
|
|
||||||
|
instance PersistFieldSql AvsFullCardNo where
|
||||||
|
sqlType _ = SqlString
|
||||||
|
|
||||||
discernAvsCardPersonalNo :: Text -> Maybe (Either AvsFullCardNo AvsInternalPersonalNo) -- Just implies it is a whole number or decimal with one digit after the point
|
discernAvsCardPersonalNo :: Text -> Maybe (Either AvsFullCardNo AvsInternalPersonalNo) -- Just implies it is a whole number or decimal with one digit after the point
|
||||||
discernAvsCardPersonalNo (Text.span Char.isDigit -> (c, pv))
|
discernAvsCardPersonalNo (Text.span Char.isDigit -> (c, pv))
|
||||||
| Text.null pv
|
| Text.null pv
|
||||||
|
|||||||
13
src/Utils.hs
13
src/Utils.hs
@ -1022,6 +1022,19 @@ actRight (Right y) f = f y
|
|||||||
-- Exception --
|
-- Exception --
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
-- maybeCatchAll :: MonadCatch m => m a -> m (Maybe a)
|
||||||
|
-- maybeCatchAll act = catch (Just <$> act) ignore
|
||||||
|
-- where
|
||||||
|
-- ignore :: Monad m => SomeException -> m (Maybe a)
|
||||||
|
-- ignore _ = return Nothing
|
||||||
|
|
||||||
|
-- | Ignore all errors by returning Nothing. (Not sure if this function is a good idea)
|
||||||
|
maybeCatchAll :: MonadCatch m => m (Maybe a) -> m (Maybe a)
|
||||||
|
maybeCatchAll act = catch act ignore
|
||||||
|
where
|
||||||
|
ignore :: Monad m => SomeException -> m (Maybe a)
|
||||||
|
ignore _ = return Nothing
|
||||||
|
|
||||||
maybeExceptT :: Monad m => e -> m (Maybe b) -> ExceptT e m b
|
maybeExceptT :: Monad m => e -> m (Maybe b) -> ExceptT e m b
|
||||||
maybeExceptT err act = lift act >>= maybe (throwE err) return
|
maybeExceptT err act = lift act >>= maybe (throwE err) return
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user