core: agent users (#1727)

This commit is contained in:
JRoberts 2023-01-13 13:54:07 +04:00 committed by GitHub
parent 7323bb4333
commit 424328b9d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 132 additions and 61 deletions

View File

@ -481,7 +481,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
} }
suspend fun testSMPServer(smpServer: String): SMPTestFailure? { suspend fun testSMPServer(smpServer: String): SMPTestFailure? {
val r = sendCmd(CC.TestSMPServer(smpServer)) val userId = chatModel.currentUser.value?.userId ?: run { throw Exception("testSMPServer: no current user") }
val r = sendCmd(CC.TestSMPServer(userId, smpServer))
return when (r) { return when (r) {
is CR.SmpTestResult -> r.smpTestFailure is CR.SmpTestResult -> r.smpTestFailure
else -> { else -> {
@ -1615,7 +1616,7 @@ sealed class CC {
class APIGetGroupLink(val groupId: Long): CC() class APIGetGroupLink(val groupId: Long): CC()
class APIGetUserSMPServers(val userId: Long): CC() class APIGetUserSMPServers(val userId: Long): CC()
class APISetUserSMPServers(val userId: Long, val smpServers: List<ServerCfg>): CC() class APISetUserSMPServers(val userId: Long, val smpServers: List<ServerCfg>): CC()
class TestSMPServer(val smpServer: String): CC() class TestSMPServer(val userId: Long, val smpServer: String): CC()
class APISetChatItemTTL(val userId: Long, val seconds: Long?): CC() class APISetChatItemTTL(val userId: Long, val seconds: Long?): CC()
class APIGetChatItemTTL(val userId: Long): CC() class APIGetChatItemTTL(val userId: Long): CC()
class APISetNetworkConfig(val networkConfig: NetCfg): CC() class APISetNetworkConfig(val networkConfig: NetCfg): CC()
@ -1686,7 +1687,7 @@ sealed class CC {
is APIGetGroupLink -> "/_get link #$groupId" is APIGetGroupLink -> "/_get link #$groupId"
is APIGetUserSMPServers -> "/_smp $userId" is APIGetUserSMPServers -> "/_smp $userId"
is APISetUserSMPServers -> "/_smp $userId ${smpServersStr(smpServers)}" is APISetUserSMPServers -> "/_smp $userId ${smpServersStr(smpServers)}"
is TestSMPServer -> "/smp test $smpServer" is TestSMPServer -> "/smp test $userId $smpServer"
is APISetChatItemTTL -> "/_ttl $userId ${chatItemTTLStr(seconds)}" is APISetChatItemTTL -> "/_ttl $userId ${chatItemTTLStr(seconds)}"
is APIGetChatItemTTL -> "/_ttl $userId" is APIGetChatItemTTL -> "/_ttl $userId"
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}" is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"

View File

@ -323,7 +323,8 @@ func setUserSMPServers(smpServers: [ServerCfg]) async throws {
} }
func testSMPServer(smpServer: String) async throws -> Result<(), SMPTestFailure> { func testSMPServer(smpServer: String) async throws -> Result<(), SMPTestFailure> {
let r = await chatSendCmd(.testSMPServer(smpServer: smpServer)) guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("testSMPServer: no current user") }
let r = await chatSendCmd(.testSMPServer(userId: userId, smpServer: smpServer))
if case let .smpTestResult(testFailure) = r { if case let .smpTestResult(testFailure) = r {
if let t = testFailure { if let t = testFailure {
return .failure(t) return .failure(t)

View File

@ -48,7 +48,7 @@ public enum ChatCommand {
case apiGetGroupLink(groupId: Int64) case apiGetGroupLink(groupId: Int64)
case apiGetUserSMPServers(userId: Int64) case apiGetUserSMPServers(userId: Int64)
case apiSetUserSMPServers(userId: Int64, smpServers: [ServerCfg]) case apiSetUserSMPServers(userId: Int64, smpServers: [ServerCfg])
case testSMPServer(smpServer: String) case testSMPServer(userId: Int64, smpServer: String)
case apiSetChatItemTTL(userId: Int64, seconds: Int64?) case apiSetChatItemTTL(userId: Int64, seconds: Int64?)
case apiGetChatItemTTL(userId: Int64) case apiGetChatItemTTL(userId: Int64)
case apiSetNetworkConfig(networkConfig: NetCfg) case apiSetNetworkConfig(networkConfig: NetCfg)
@ -132,7 +132,7 @@ public enum ChatCommand {
case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)" case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)"
case let .apiGetUserSMPServers(userId): return "/_smp \(userId)" case let .apiGetUserSMPServers(userId): return "/_smp \(userId)"
case let .apiSetUserSMPServers(userId, smpServers): return "/_smp \(userId) \(smpServersStr(smpServers: smpServers))" case let .apiSetUserSMPServers(userId, smpServers): return "/_smp \(userId) \(smpServersStr(smpServers: smpServers))"
case let .testSMPServer(smpServer): return "/smp test \(smpServer)" case let .testSMPServer(userId, smpServer): return "/smp test \(userId) \(smpServer)"
case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))" case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))"
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)" case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))" case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"

View File

@ -7,7 +7,12 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package source-repository-package
type: git type: git
location: https://github.com/simplex-chat/simplexmq.git location: https://github.com/simplex-chat/simplexmq.git
tag: 058e3ac55e8577280267f9341ccd7d3e971bc51a tag: 8e024590bc2b4428e64e625a9c2392908fc5912e
source-repository-package
type: git
location: https://github.com/simplex-chat/hs-socks.git
tag: a30cc7a79a08d8108316094f8f2f82a0c5e1ac51
source-repository-package source-repository-package
type: git type: git

View File

@ -1,5 +1,6 @@
{ {
"https://github.com/simplex-chat/simplexmq.git"."058e3ac55e8577280267f9341ccd7d3e971bc51a" = "1rw0j3d5higdrq5klsgnj8b8zfh08g5zv72hqcm7wkw1mmllpfrk"; "https://github.com/simplex-chat/simplexmq.git"."8e024590bc2b4428e64e625a9c2392908fc5912e" = "0rgsf1jz2dpqbdpdfpajsi8gry47jl8jqgw13dfxr3ll9v7pr4sf";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd"; "https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0"; "https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
"https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp"; "https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";

View File

@ -74,6 +74,7 @@ library
Simplex.Chat.Migrations.M20221223_idx_chat_items_item_status Simplex.Chat.Migrations.M20221223_idx_chat_items_item_status
Simplex.Chat.Migrations.M20221230_idxs Simplex.Chat.Migrations.M20221230_idxs
Simplex.Chat.Migrations.M20230107_connections_auth_err_counter Simplex.Chat.Migrations.M20230107_connections_auth_err_counter
Simplex.Chat.Migrations.M20230111_users_agent_user_id
Simplex.Chat.Mobile Simplex.Chat.Mobile
Simplex.Chat.Options Simplex.Chat.Options
Simplex.Chat.ProfileGenerator Simplex.Chat.ProfileGenerator

View File

@ -94,7 +94,7 @@ defaultChatConfig =
}, },
yesToMigrations = False, yesToMigrations = False,
defaultServers = defaultServers =
InitialAgentServers DefaultAgentServers
{ smp = _defaultSMPServers, { smp = _defaultSMPServers,
ntf = _defaultNtfServers, ntf = _defaultNtfServers,
netCfg = defaultNetworkConfig netCfg = defaultNetworkConfig
@ -162,19 +162,25 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen
showLiveItems <- newTVarIO False showLiveItems <- newTVarIO False
pure ChatController {activeTo, firstTime, currentUser, smpAgent, agentAsync, chatStore, chatStoreChanged, idsDrg, inputQ, outputQ, notifyQ, chatLock, sndFiles, rcvFiles, currentCalls, config, sendNotification, incognitoMode, filesFolder, expireCIsAsync, expireCIs, cleanupManagerAsync, timedItemThreads, showLiveItems} pure ChatController {activeTo, firstTime, currentUser, smpAgent, agentAsync, chatStore, chatStoreChanged, idsDrg, inputQ, outputQ, notifyQ, chatLock, sndFiles, rcvFiles, currentCalls, config, sendNotification, incognitoMode, filesFolder, expireCIsAsync, expireCIs, cleanupManagerAsync, timedItemThreads, showLiveItems}
where where
configServers :: InitialAgentServers configServers :: DefaultAgentServers
configServers = configServers =
let smp' = fromMaybe (smp defaultServers) (nonEmpty smpServers) let smp' = fromMaybe (smp (defaultServers :: DefaultAgentServers)) (nonEmpty smpServers)
in defaultServers {smp = smp', netCfg = networkConfig} in defaultServers {smp = smp', netCfg = networkConfig}
agentServers :: ChatConfig -> IO InitialAgentServers agentServers :: ChatConfig -> IO InitialAgentServers
agentServers config@ChatConfig {defaultServers = ss@InitialAgentServers {smp}} = do agentServers config@ChatConfig {defaultServers = DefaultAgentServers {smp, ntf, netCfg}} = do
smp' <- maybe (pure smp) userServers user users <- withTransaction chatStore getUsers
pure ss {smp = smp'} smp' <- case users of
[] -> pure $ M.fromList [(1, smp)]
_ -> M.fromList <$> initialServers users
pure InitialAgentServers {smp = smp', ntf, netCfg}
where where
initialServers :: [User] -> IO [(UserId, NonEmpty SMPServerWithAuth)]
initialServers = mapM (\u -> (aUserId u,) <$> userServers u)
userServers :: User -> IO (NonEmpty SMPServerWithAuth)
userServers user' = activeAgentServers config <$> withTransaction chatStore (`getSMPServers` user') userServers user' = activeAgentServers config <$> withTransaction chatStore (`getSMPServers` user')
activeAgentServers :: ChatConfig -> [ServerCfg] -> NonEmpty SMPServerWithAuth activeAgentServers :: ChatConfig -> [ServerCfg] -> NonEmpty SMPServerWithAuth
activeAgentServers ChatConfig {defaultServers = InitialAgentServers {smp}} = activeAgentServers ChatConfig {defaultServers = DefaultAgentServers {smp}} =
fromMaybe smp fromMaybe smp
. nonEmpty . nonEmpty
. map (\ServerCfg {server} -> server) . map (\ServerCfg {server} -> server)
@ -264,11 +270,17 @@ processChatCommand = \case
ShowActiveUser -> withUser' $ pure . CRActiveUser ShowActiveUser -> withUser' $ pure . CRActiveUser
CreateActiveUser p -> do CreateActiveUser p -> do
u <- asks currentUser u <- asks currentUser
user <- withStore $ \db -> createUser db p True -- TODO option to choose current user servers
DefaultAgentServers {smp} <- asks $ defaultServers . config
auId <-
withStore' getUsers >>= \case
[] -> pure 1
_ -> withAgent (`createUser` smp)
user <- withStore $ \db -> createUserRecord db (AgentUserId auId) p True
atomically . writeTVar u $ Just user atomically . writeTVar u $ Just user
pure $ CRActiveUser user pure $ CRActiveUser user
ListUsers -> do ListUsers -> do
users <- withStore' $ \db -> getUsers db users <- withStore' getUsers
pure $ CRUsersList users pure $ CRUsersList users
APISetActiveUser userId -> do APISetActiveUser userId -> do
u <- asks currentUser u <- asks currentUser
@ -359,7 +371,7 @@ processChatCommand = \case
(agentConnId_, fileConnReq) <- (agentConnId_, fileConnReq) <-
if isJust fileInline if isJust fileInline
then pure (Nothing, Nothing) then pure (Nothing, Nothing)
else bimap Just Just <$> withAgent (\a -> createConnection a True SCMInvitation Nothing) else bimap Just Just <$> withAgent (\a -> createConnection a (aUserId user) True SCMInvitation Nothing)
let fileName = takeFileName file let fileName = takeFileName file
fileInvitation = FileInvitation {fileName, fileSize, fileConnReq, fileInline} fileInvitation = FileInvitation {fileName, fileSize, fileConnReq, fileInline}
withStore' $ \db -> do withStore' $ \db -> do
@ -773,7 +785,7 @@ processChatCommand = \case
pure CRNtfMessages {user, connEntity, msgTs = msgTs', ntfMessages} pure CRNtfMessages {user, connEntity, msgTs = msgTs', ntfMessages}
APIGetUserSMPServers cmdUserId -> withUser $ \user -> do APIGetUserSMPServers cmdUserId -> withUser $ \user -> do
checkCorrectCmdUser cmdUserId user checkCorrectCmdUser cmdUserId user
ChatConfig {defaultServers = InitialAgentServers {smp = defaultSMPServers}} <- asks config ChatConfig {defaultServers = DefaultAgentServers {smp = defaultSMPServers}} <- asks config
smpServers <- withStore' (`getSMPServers` user) smpServers <- withStore' (`getSMPServers` user)
let smpServers' = fromMaybe (L.map toServerCfg defaultSMPServers) $ nonEmpty smpServers let smpServers' = fromMaybe (L.map toServerCfg defaultSMPServers) $ nonEmpty smpServers
pure $ CRUserSMPServers user smpServers' defaultSMPServers pure $ CRUserSMPServers user smpServers' defaultSMPServers
@ -785,11 +797,13 @@ processChatCommand = \case
checkCorrectCmdUser cmdUserId user checkCorrectCmdUser cmdUserId user
withStore $ \db -> overwriteSMPServers db user smpServers withStore $ \db -> overwriteSMPServers db user smpServers
cfg <- asks config cfg <- asks config
withAgent $ \a -> setSMPServers a $ activeAgentServers cfg smpServers withAgent $ \a -> setSMPServers a (aUserId user) $ activeAgentServers cfg smpServers
pure $ CRCmdOk (Just user) pure $ CRCmdOk (Just user)
SetUserSMPServers smpServersConfig -> withUser $ \User {userId} -> SetUserSMPServers smpServersConfig -> withUser $ \User {userId} ->
processChatCommand $ APISetUserSMPServers userId smpServersConfig processChatCommand $ APISetUserSMPServers userId smpServersConfig
TestSMPServer smpServer -> CRSmpTestResult <$> withAgent (`testSMPServerConnection` smpServer) TestSMPServer cmdUserId smpServer -> withUser $ \user -> do
checkCorrectCmdUser cmdUserId user
CRSmpTestResult <$> (withAgent $ \a -> testSMPServerConnection a (aUserId user) smpServer)
APISetChatItemTTL cmdUserId newTTL_ -> withUser' $ \user -> do APISetChatItemTTL cmdUserId newTTL_ -> withUser' $ \user -> do
checkCorrectCmdUser cmdUserId user checkCorrectCmdUser cmdUserId user
checkStoreNotChanged $ checkStoreNotChanged $
@ -921,7 +935,7 @@ processChatCommand = \case
-- [incognito] generate profile for connection -- [incognito] generate profile for connection
incognito <- readTVarIO =<< asks incognitoMode incognito <- readTVarIO =<< asks incognitoMode
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
(connId, cReq) <- withAgent $ \a -> createConnection a True SCMInvitation Nothing (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing
conn <- withStore' $ \db -> createDirectConnection db userId connId cReq ConnNew incognitoProfile conn <- withStore' $ \db -> createDirectConnection db userId connId cReq ConnNew incognitoProfile
toView $ CRNewContactConnection user conn toView $ CRNewContactConnection user conn
pure $ CRInvitation user cReq pure $ CRInvitation user cReq
@ -933,7 +947,7 @@ processChatCommand = \case
incognito <- readTVarIO =<< asks incognitoMode incognito <- readTVarIO =<< asks incognitoMode
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
let profileToSend = userProfileToSend user incognitoProfile Nothing let profileToSend = userProfileToSend user incognitoProfile Nothing
connId <- withAgent $ \a -> joinConnection a True cReq . directMessage $ XInfo profileToSend connId <- withAgent $ \a -> joinConnection a (aUserId user) True cReq . directMessage $ XInfo profileToSend
conn <- withStore' $ \db -> createDirectConnection db userId connId cReq ConnJoined $ incognitoProfile $> profileToSend conn <- withStore' $ \db -> createDirectConnection db userId connId cReq ConnJoined $ incognitoProfile $> profileToSend
toView $ CRNewContactConnection user conn toView $ CRNewContactConnection user conn
pure $ CRSentConfirmation user pure $ CRSentConfirmation user
@ -957,7 +971,7 @@ processChatCommand = \case
processChatCommand $ APIListContacts userId processChatCommand $ APIListContacts userId
APICreateMyAddress cmdUserId -> withUser $ \user@User {userId} -> withChatLock "createMyAddress" . procCmd $ do APICreateMyAddress cmdUserId -> withUser $ \user@User {userId} -> withChatLock "createMyAddress" . procCmd $ do
checkCorrectCmdUser cmdUserId user checkCorrectCmdUser cmdUserId user
(connId, cReq) <- withAgent $ \a -> createConnection a True SCMContact Nothing (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing
withStore $ \db -> createUserContactLink db userId connId cReq withStore $ \db -> createUserContactLink db userId connId cReq
pure $ CRUserContactLinkCreated user cReq pure $ CRUserContactLinkCreated user cReq
CreateMyAddress -> withUser $ \User {userId} -> CreateMyAddress -> withUser $ \User {userId} ->
@ -1047,7 +1061,7 @@ processChatCommand = \case
case contactMember contact members of case contactMember contact members of
Nothing -> do Nothing -> do
gVar <- asks idsDrg gVar <- asks idsDrg
(agentConnId, cReq) <- withAgent $ \a -> createConnection a True SCMInvitation Nothing (agentConnId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing
member <- withStore $ \db -> createNewContactMember db gVar user groupId contact memRole agentConnId cReq member <- withStore $ \db -> createNewContactMember db gVar user groupId contact memRole agentConnId cReq
sendInvitation member cReq sendInvitation member cReq
pure $ CRSentGroupInvitation user gInfo contact member pure $ CRSentGroupInvitation user gInfo contact member
@ -1063,7 +1077,7 @@ processChatCommand = \case
APIJoinGroup groupId -> withUser $ \user@User {userId} -> do APIJoinGroup groupId -> withUser $ \user@User {userId} -> do
ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g@GroupInfo {membership}} <- withStore $ \db -> getGroupInvitation db user groupId ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g@GroupInfo {membership}} <- withStore $ \db -> getGroupInvitation db user groupId
withChatLock "joinGroup" . procCmd $ do withChatLock "joinGroup" . procCmd $ do
agentConnId <- withAgent $ \a -> joinConnection a True connRequest . directMessage $ XGrpAcpt (memberId (membership :: GroupMember)) agentConnId <- withAgent $ \a -> joinConnection a (aUserId user) True connRequest . directMessage $ XGrpAcpt (memberId (membership :: GroupMember))
withStore' $ \db -> do withStore' $ \db -> do
createMemberConnection db userId fromMember agentConnId createMemberConnection db userId fromMember agentConnId
updateGroupMemberStatus db userId fromMember GSMemAccepted updateGroupMemberStatus db userId fromMember GSMemAccepted
@ -1180,7 +1194,7 @@ processChatCommand = \case
unless (memberActive membership) $ throwChatError CEGroupMemberNotActive unless (memberActive membership) $ throwChatError CEGroupMemberNotActive
groupLinkId <- GroupLinkId <$> (asks idsDrg >>= liftIO . (`randomBytes` 16)) groupLinkId <- GroupLinkId <$> (asks idsDrg >>= liftIO . (`randomBytes` 16))
let crClientData = encodeJSON $ CRDataGroup groupLinkId let crClientData = encodeJSON $ CRDataGroup groupLinkId
(connId, cReq) <- withAgent $ \a -> createConnection a True SCMContact $ Just crClientData (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact $ Just crClientData
withStore $ \db -> createGroupLink db user gInfo connId cReq groupLinkId withStore $ \db -> createGroupLink db user gInfo connId cReq groupLinkId
pure $ CRGroupLinkCreated user gInfo cReq pure $ CRGroupLinkCreated user gInfo cReq
APIDeleteGroupLink groupId -> withUser $ \user -> withChatLock "deleteGroupLink" $ do APIDeleteGroupLink groupId -> withUser $ \user -> withChatLock "deleteGroupLink" $ do
@ -1388,7 +1402,7 @@ processChatCommand = \case
incognito <- readTVarIO =<< asks incognitoMode incognito <- readTVarIO =<< asks incognitoMode
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
let profileToSend = userProfileToSend user incognitoProfile Nothing let profileToSend = userProfileToSend user incognitoProfile Nothing
connId <- withAgent $ \a -> joinConnection a True cReq $ directMessage (XContact profileToSend $ Just xContactId) connId <- withAgent $ \a -> joinConnection a (aUserId user) True cReq $ directMessage (XContact profileToSend $ Just xContactId)
let groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli let groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId
toView $ CRNewContactConnection user conn toView $ CRNewContactConnection user conn
@ -3563,13 +3577,13 @@ markGroupCIDeleted user gInfo ci@(CChatItem msgDir deletedItem) msgId byUser = d
createAgentConnectionAsync :: forall m c. (ChatMonad m, ConnectionModeI c) => User -> CommandFunction -> Bool -> SConnectionMode c -> m (CommandId, ConnId) createAgentConnectionAsync :: forall m c. (ChatMonad m, ConnectionModeI c) => User -> CommandFunction -> Bool -> SConnectionMode c -> m (CommandId, ConnId)
createAgentConnectionAsync user cmdFunction enableNtfs cMode = do createAgentConnectionAsync user cmdFunction enableNtfs cMode = do
cmdId <- withStore' $ \db -> createCommand db user Nothing cmdFunction cmdId <- withStore' $ \db -> createCommand db user Nothing cmdFunction
connId <- withAgent $ \a -> createConnectionAsync a (aCorrId cmdId) enableNtfs cMode connId <- withAgent $ \a -> createConnectionAsync a (aUserId user) (aCorrId cmdId) enableNtfs cMode
pure (cmdId, connId) pure (cmdId, connId)
joinAgentConnectionAsync :: ChatMonad m => User -> Bool -> ConnectionRequestUri c -> ConnInfo -> m (CommandId, ConnId) joinAgentConnectionAsync :: ChatMonad m => User -> Bool -> ConnectionRequestUri c -> ConnInfo -> m (CommandId, ConnId)
joinAgentConnectionAsync user enableNtfs cReqUri cInfo = do joinAgentConnectionAsync user enableNtfs cReqUri cInfo = do
cmdId <- withStore' $ \db -> createCommand db user Nothing CFJoinConn cmdId <- withStore' $ \db -> createCommand db user Nothing CFJoinConn
connId <- withAgent $ \a -> joinConnectionAsync a (aCorrId cmdId) enableNtfs cReqUri cInfo connId <- withAgent $ \a -> joinConnectionAsync a (aUserId user) (aCorrId cmdId) enableNtfs cReqUri cInfo
pure (cmdId, connId) pure (cmdId, connId)
allowAgentConnectionAsync :: (MsgEncodingI e, ChatMonad m) => User -> Connection -> ConfirmationId -> ChatMsgEvent e -> m () allowAgentConnectionAsync :: (MsgEncodingI e, ChatMonad m) => User -> Connection -> ConfirmationId -> ChatMsgEvent e -> m ()
@ -3684,7 +3698,7 @@ getCreateActiveUser st = do
loop = do loop = do
displayName <- getContactName displayName <- getContactName
fullName <- T.pack <$> getWithPrompt "full name (optional)" fullName <- T.pack <$> getWithPrompt "full name (optional)"
withTransaction st (\db -> runExceptT $ createUser db Profile {displayName, fullName, image = Nothing, preferences = Nothing} True) >>= \case withTransaction st (\db -> runExceptT $ createUserRecord db (AgentUserId 1) Profile {displayName, fullName, image = Nothing, preferences = Nothing} True) >>= \case
Left SEDuplicateName -> do Left SEDuplicateName -> do
putStrLn "chosen display name is already used by another profile on this device, choose another one" putStrLn "chosen display name is already used by another profile on this device, choose another one"
loop loop
@ -3848,7 +3862,7 @@ chatCommandP =
"/smp_servers " *> (SetUserSMPServers . SMPServersConfig . map toServerCfg <$> smpServersP), "/smp_servers " *> (SetUserSMPServers . SMPServersConfig . map toServerCfg <$> smpServersP),
"/smp_servers" $> GetUserSMPServers, "/smp_servers" $> GetUserSMPServers,
"/smp default" $> SetUserSMPServers (SMPServersConfig []), "/smp default" $> SetUserSMPServers (SMPServersConfig []),
"/smp test " *> (TestSMPServer <$> strP), "/smp test " *> (TestSMPServer <$> A.decimal <* A.space <*> strP),
"/_smp " *> (APISetUserSMPServers <$> A.decimal <* A.space <*> jsonP), "/_smp " *> (APISetUserSMPServers <$> A.decimal <* A.space <*> jsonP),
"/smp " *> (SetUserSMPServers . SMPServersConfig . map toServerCfg <$> smpServersP), "/smp " *> (SetUserSMPServers . SMPServersConfig . map toServerCfg <$> smpServersP),
"/_smp " *> (APIGetUserSMPServers <$> A.decimal), "/_smp " *> (APIGetUserSMPServers <$> A.decimal),

View File

@ -43,7 +43,7 @@ import Simplex.Chat.Store (AutoAccept, StoreError, UserContactLink)
import Simplex.Chat.Types import Simplex.Chat.Types
import Simplex.Messaging.Agent (AgentClient) import Simplex.Messaging.Agent (AgentClient)
import Simplex.Messaging.Agent.Client (AgentLocks, SMPTestFailure) import Simplex.Messaging.Agent.Client (AgentLocks, SMPTestFailure)
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, InitialAgentServers, NetworkConfig) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, NetworkConfig)
import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Lock
import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore)
@ -51,7 +51,7 @@ import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Encoding.String import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON) import Simplex.Messaging.Parsers (dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtocolType, CorrId, MsgFlags) import Simplex.Messaging.Protocol (AProtocolType, CorrId, MsgFlags, NtfServer)
import Simplex.Messaging.TMap (TMap) import Simplex.Messaging.TMap (TMap)
import Simplex.Messaging.Transport.Client (TransportHost) import Simplex.Messaging.Transport.Client (TransportHost)
import System.IO (Handle) import System.IO (Handle)
@ -70,7 +70,7 @@ updateStr = "To update run: curl -o- https://raw.githubusercontent.com/simplex-c
data ChatConfig = ChatConfig data ChatConfig = ChatConfig
{ agentConfig :: AgentConfig, { agentConfig :: AgentConfig,
yesToMigrations :: Bool, yesToMigrations :: Bool,
defaultServers :: InitialAgentServers, defaultServers :: DefaultAgentServers,
tbqSize :: Natural, tbqSize :: Natural,
fileChunkSize :: Integer, fileChunkSize :: Integer,
inlineFiles :: InlineFilesConfig, inlineFiles :: InlineFilesConfig,
@ -80,6 +80,12 @@ data ChatConfig = ChatConfig
testView :: Bool testView :: Bool
} }
data DefaultAgentServers = DefaultAgentServers
{ smp :: NonEmpty SMPServerWithAuth,
ntf :: [NtfServer],
netCfg :: NetworkConfig
}
data InlineFilesConfig = InlineFilesConfig data InlineFilesConfig = InlineFilesConfig
{ offerChunks :: Integer, { offerChunks :: Integer,
sendChunks :: Integer, sendChunks :: Integer,
@ -203,7 +209,7 @@ data ChatCommand
| GetUserSMPServers | GetUserSMPServers
| APISetUserSMPServers UserId SMPServersConfig | APISetUserSMPServers UserId SMPServersConfig
| SetUserSMPServers SMPServersConfig | SetUserSMPServers SMPServersConfig
| TestSMPServer SMPServerWithAuth | TestSMPServer UserId SMPServerWithAuth
| APISetChatItemTTL UserId (Maybe Int64) | APISetChatItemTTL UserId (Maybe Int64)
| SetChatItemTTL (Maybe Int64) | SetChatItemTTL (Maybe Int64)
| APIGetChatItemTTL UserId | APIGetChatItemTTL UserId

View File

@ -0,0 +1,17 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Migrations.M20230111_users_agent_user_id where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
m20230111_users_agent_user_id :: Query
m20230111_users_agent_user_id =
[sql|
PRAGMA ignore_check_constraints=ON;
ALTER TABLE users ADD COLUMN agent_user_id INTEGER CHECK (agent_user_id NOT NULL);
UPDATE users SET agent_user_id = 1;
PRAGMA ignore_check_constraints=OFF;
|]

View File

@ -29,7 +29,8 @@ CREATE TABLE users(
local_display_name TEXT NOT NULL UNIQUE, local_display_name TEXT NOT NULL UNIQUE,
active_user INTEGER NOT NULL DEFAULT 0, active_user INTEGER NOT NULL DEFAULT 0,
created_at TEXT CHECK(created_at NOT NULL), created_at TEXT CHECK(created_at NOT NULL),
updated_at TEXT CHECK(updated_at NOT NULL), -- 1 for active user updated_at TEXT CHECK(updated_at NOT NULL),
agent_user_id INTEGER CHECK(agent_user_id NOT NULL), -- 1 for active user
FOREIGN KEY(user_id, local_display_name) FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name)
ON DELETE CASCADE ON DELETE CASCADE

View File

@ -25,7 +25,7 @@ module Simplex.Chat.Store
createChatStore, createChatStore,
chatStoreFile, chatStoreFile,
agentStoreFile, agentStoreFile,
createUser, createUserRecord,
getUsers, getUsers,
setActiveUser, setActiveUser,
getSetActiveUser, getSetActiveUser,
@ -330,6 +330,7 @@ import Simplex.Chat.Migrations.M20221222_chat_ts
import Simplex.Chat.Migrations.M20221223_idx_chat_items_item_status import Simplex.Chat.Migrations.M20221223_idx_chat_items_item_status
import Simplex.Chat.Migrations.M20221230_idxs import Simplex.Chat.Migrations.M20221230_idxs
import Simplex.Chat.Migrations.M20230107_connections_auth_err_counter import Simplex.Chat.Migrations.M20230107_connections_auth_err_counter
import Simplex.Chat.Migrations.M20230111_users_agent_user_id
import Simplex.Chat.Protocol import Simplex.Chat.Protocol
import Simplex.Chat.Types import Simplex.Chat.Types
import Simplex.Chat.Util (week) import Simplex.Chat.Util (week)
@ -390,7 +391,8 @@ schemaMigrations =
("20221222_chat_ts", m20221222_chat_ts), ("20221222_chat_ts", m20221222_chat_ts),
("20221223_idx_chat_items_item_status", m20221223_idx_chat_items_item_status), ("20221223_idx_chat_items_item_status", m20221223_idx_chat_items_item_status),
("20221230_idxs", m20221230_idxs), ("20221230_idxs", m20221230_idxs),
("20230107_connections_auth_err_counter", m20230107_connections_auth_err_counter) ("20230107_connections_auth_err_counter", m20230107_connections_auth_err_counter),
("20230111_users_agent_user_id", m20230111_users_agent_user_id)
] ]
-- | The list of migrations in ascending order by date -- | The list of migrations in ascending order by date
@ -419,15 +421,15 @@ handleSQLError err e
insertedRowId :: DB.Connection -> IO Int64 insertedRowId :: DB.Connection -> IO Int64
insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()" insertedRowId db = fromOnly . head <$> DB.query_ db "SELECT last_insert_rowid()"
createUser :: DB.Connection -> Profile -> Bool -> ExceptT StoreError IO User createUserRecord :: DB.Connection -> AgentUserId -> Profile -> Bool -> ExceptT StoreError IO User
createUser db Profile {displayName, fullName, image, preferences = userPreferences} activeUser = createUserRecord db (AgentUserId auId) Profile {displayName, fullName, image, preferences = userPreferences} activeUser =
checkConstraint SEDuplicateName . liftIO $ do checkConstraint SEDuplicateName . liftIO $ do
currentTs <- getCurrentTime currentTs <- getCurrentTime
when activeUser $ DB.execute_ db "UPDATE users SET active_user = 0" when activeUser $ DB.execute_ db "UPDATE users SET active_user = 0"
DB.execute DB.execute
db db
"INSERT INTO users (local_display_name, active_user, contact_id, created_at, updated_at) VALUES (?,?,0,?,?)" "INSERT INTO users (agent_user_id, local_display_name, active_user, contact_id, created_at, updated_at) VALUES (?,?,?,0,?,?)"
(displayName, activeUser, currentTs, currentTs) (auId, displayName, activeUser, currentTs, currentTs)
userId <- insertedRowId db userId <- insertedRowId db
DB.execute DB.execute
db db
@ -444,7 +446,7 @@ createUser db Profile {displayName, fullName, image, preferences = userPreferenc
(profileId, displayName, userId, True, currentTs, currentTs) (profileId, displayName, userId, True, currentTs, currentTs)
contactId <- insertedRowId db contactId <- insertedRowId db
DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?" (contactId, userId) DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?" (contactId, userId)
pure $ toUser (userId, contactId, profileId, activeUser, displayName, fullName, image, userPreferences) pure $ toUser (userId, auId, contactId, profileId, activeUser, displayName, fullName, image, userPreferences)
getUsers :: DB.Connection -> IO [User] getUsers :: DB.Connection -> IO [User]
getUsers db = getUsers db =
@ -453,16 +455,16 @@ getUsers db =
userQuery :: Query userQuery :: Query
userQuery = userQuery =
[sql| [sql|
SELECT u.user_id, u.contact_id, cp.contact_profile_id, u.active_user, u.local_display_name, cp.full_name, cp.image, cp.preferences SELECT u.user_id, u.agent_user_id, u.contact_id, cp.contact_profile_id, u.active_user, u.local_display_name, cp.full_name, cp.image, cp.preferences
FROM users u FROM users u
JOIN contacts ct ON ct.contact_id = u.contact_id JOIN contacts ct ON ct.contact_id = u.contact_id
JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id
|] |]
toUser :: (UserId, ContactId, ProfileId, Bool, ContactName, Text, Maybe ImageData, Maybe Preferences) -> User toUser :: (UserId, UserId, ContactId, ProfileId, Bool, ContactName, Text, Maybe ImageData, Maybe Preferences) -> User
toUser (userId, userContactId, profileId, activeUser, displayName, fullName, image, userPreferences) = toUser (userId, auId, userContactId, profileId, activeUser, displayName, fullName, image, userPreferences) =
let profile = LocalProfile {profileId, displayName, fullName, image, preferences = userPreferences, localAlias = ""} let profile = LocalProfile {profileId, displayName, fullName, image, preferences = userPreferences, localAlias = ""}
in User {userId, userContactId, localDisplayName = displayName, profile, activeUser, fullPreferences = mergePreferences Nothing userPreferences} in User {userId, agentUserId = AgentUserId auId, userContactId, localDisplayName = displayName, profile, activeUser, fullPreferences = mergePreferences Nothing userPreferences}
setActiveUser :: DB.Connection -> UserId -> IO () setActiveUser :: DB.Connection -> UserId -> IO ()
setActiveUser db userId = do setActiveUser db userId = do

View File

@ -17,7 +17,6 @@ import Simplex.Chat.Options
import Simplex.Chat.Terminal.Input import Simplex.Chat.Terminal.Input
import Simplex.Chat.Terminal.Notification import Simplex.Chat.Terminal.Notification
import Simplex.Chat.Terminal.Output import Simplex.Chat.Terminal.Output
import Simplex.Messaging.Agent.Env.SQLite (InitialAgentServers (..))
import Simplex.Messaging.Client (defaultNetworkConfig) import Simplex.Messaging.Client (defaultNetworkConfig)
import Simplex.Messaging.Util (raceAny_) import Simplex.Messaging.Util (raceAny_)
import System.Exit (exitFailure) import System.Exit (exitFailure)
@ -26,7 +25,7 @@ terminalChatConfig :: ChatConfig
terminalChatConfig = terminalChatConfig =
defaultChatConfig defaultChatConfig
{ defaultServers = { defaultServers =
InitialAgentServers DefaultAgentServers
{ smp = { smp =
L.fromList L.fromList
[ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion", [ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",

View File

@ -80,8 +80,31 @@ instance IsContact Contact where
preferences' Contact {profile = LocalProfile {preferences}} = preferences preferences' Contact {profile = LocalProfile {preferences}} = preferences
{-# INLINE preferences' #-} {-# INLINE preferences' #-}
newtype AgentUserId = AgentUserId UserId
deriving (Eq, Show)
instance StrEncoding AgentUserId where
strEncode (AgentUserId uId) = strEncode uId
strDecode s = AgentUserId <$> strDecode s
strP = AgentUserId <$> strP
instance FromJSON AgentUserId where
parseJSON = strParseJSON "AgentUserId"
instance ToJSON AgentUserId where
toJSON = strToJSON
toEncoding = strToJEncoding
instance FromField AgentUserId where fromField f = AgentUserId <$> fromField f
instance ToField AgentUserId where toField (AgentUserId uId) = toField uId
aUserId :: User -> UserId
aUserId User {agentUserId = AgentUserId uId} = uId
data User = User data User = User
{ userId :: UserId, { userId :: UserId,
agentUserId :: AgentUserId,
userContactId :: ContactId, userContactId :: ContactId,
localDisplayName :: ContactName, localDisplayName :: ContactName,
profile :: LocalProfile, profile :: LocalProfile,
@ -92,7 +115,7 @@ data User = User
instance ToJSON User where toEncoding = J.genericToEncoding J.defaultOptions instance ToJSON User where toEncoding = J.genericToEncoding J.defaultOptions
type UserId = ContactId type UserId = Int64
type ContactId = Int64 type ContactId = Int64

View File

@ -49,7 +49,7 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq # - ../simplexmq
- github: simplex-chat/simplexmq - github: simplex-chat/simplexmq
commit: 058e3ac55e8577280267f9341ccd7d3e971bc51a commit: 8e024590bc2b4428e64e625a9c2392908fc5912e
# - ../direct-sqlcipher # - ../direct-sqlcipher
- github: simplex-chat/direct-sqlcipher - github: simplex-chat/direct-sqlcipher
commit: 34309410eb2069b029b8fc1872deb1e0db123294 commit: 34309410eb2069b029b8fc1872deb1e0db123294

View File

@ -25,7 +25,7 @@ import Simplex.Chat.Options
import Simplex.Chat.Store import Simplex.Chat.Store
import Simplex.Chat.Terminal import Simplex.Chat.Terminal
import Simplex.Chat.Terminal.Output (newChatTerminal) import Simplex.Chat.Terminal.Output (newChatTerminal)
import Simplex.Chat.Types (Profile, User (..)) import Simplex.Chat.Types (AgentUserId (..), Profile, User (..))
import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Agent.Env.SQLite
import Simplex.Messaging.Agent.RetryInterval import Simplex.Messaging.Agent.RetryInterval
import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig) import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig)
@ -108,7 +108,7 @@ testCfgV1 = testCfg {agentConfig = testAgentCfgV1}
createTestChat :: ChatConfig -> ChatOpts -> String -> Profile -> IO TestCC createTestChat :: ChatConfig -> ChatOpts -> String -> Profile -> IO TestCC
createTestChat cfg opts@ChatOpts {dbKey} dbPrefix profile = do createTestChat cfg opts@ChatOpts {dbKey} dbPrefix profile = do
db@ChatDatabase {chatStore} <- createChatDatabase (testDBPrefix <> dbPrefix) dbKey False db@ChatDatabase {chatStore} <- createChatDatabase (testDBPrefix <> dbPrefix) dbKey False
Right user <- withTransaction chatStore $ \db' -> runExceptT $ createUser db' profile True Right user <- withTransaction chatStore $ \db' -> runExceptT $ createUserRecord db' (AgentUserId 1) profile True
startTestChat_ db cfg opts user startTestChat_ db cfg opts user
startTestChat :: ChatConfig -> ChatOpts -> String -> IO TestCC startTestChat :: ChatConfig -> ChatOpts -> String -> IO TestCC

View File

@ -3800,14 +3800,14 @@ testTestSMPServerConnection :: IO ()
testTestSMPServerConnection = testTestSMPServerConnection =
testChat2 aliceProfile bobProfile $ testChat2 aliceProfile bobProfile $
\alice _ -> do \alice _ -> do
alice ##> "/smp test smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=@localhost:5001" alice ##> "/smp test 1 smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=@localhost:5001"
alice <## "SMP server test passed" alice <## "SMP server test passed"
-- to test with password: -- to test with password:
-- alice <## "SMP server test failed at CreateQueue, error: SMP AUTH" -- alice <## "SMP server test failed at CreateQueue, error: SMP AUTH"
-- alice <## "Server requires authorization to create queues, check password" -- alice <## "Server requires authorization to create queues, check password"
alice ##> "/smp test smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:5001" alice ##> "/smp test 1 smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:5001"
alice <## "SMP server test passed" alice <## "SMP server test passed"
alice ##> "/smp test smp://LcJU@localhost:5001" alice ##> "/smp test 1 smp://LcJU@localhost:5001"
alice <## "SMP server test failed at Connect, error: BROKER smp://LcJU@localhost:5001 NETWORK" alice <## "SMP server test failed at Connect, error: BROKER smp://LcJU@localhost:5001 NETWORK"
alice <## "Possibly, certificate fingerprint in server address is incorrect" alice <## "Possibly, certificate fingerprint in server address is incorrect"

View File

@ -7,7 +7,7 @@ import ChatTests
import Control.Monad.Except import Control.Monad.Except
import Simplex.Chat.Mobile import Simplex.Chat.Mobile
import Simplex.Chat.Store import Simplex.Chat.Store
import Simplex.Chat.Types (Profile (..)) import Simplex.Chat.Types (AgentUserId (..), Profile (..))
import Test.Hspec import Test.Hspec
mobileTests :: Spec mobileTests :: Spec
@ -32,9 +32,9 @@ activeUserExists = "{\"resp\":{\"type\":\"chatCmdError\",\"chatError\":{\"type\"
activeUser :: String activeUser :: String
#if defined(darwin_HOST_OS) && defined(swiftJSON) #if defined(darwin_HOST_OS) && defined(swiftJSON)
activeUser = "{\"resp\":{\"activeUser\":{\"user\":{\"userId\":1,\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"no\"},\"fullDelete\":{\"allow\":\"no\"},\"voice\":{\"allow\":\"yes\"}},\"activeUser\":true}}}" activeUser = "{\"resp\":{\"activeUser\":{\"user\":{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"no\"},\"fullDelete\":{\"allow\":\"no\"},\"voice\":{\"allow\":\"yes\"}},\"activeUser\":true}}}"
#else #else
activeUser = "{\"resp\":{\"type\":\"activeUser\",\"user\":{\"userId\":1,\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"no\"},\"fullDelete\":{\"allow\":\"no\"},\"voice\":{\"allow\":\"yes\"}},\"activeUser\":true}}}" activeUser = "{\"resp\":{\"type\":\"activeUser\",\"user\":{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"no\"},\"fullDelete\":{\"allow\":\"no\"},\"voice\":{\"allow\":\"yes\"}},\"activeUser\":true}}}"
#endif #endif
chatStarted :: String chatStarted :: String
@ -93,7 +93,7 @@ testChatApi = withTmpFiles $ do
let dbPrefix = testDBPrefix <> "1" let dbPrefix = testDBPrefix <> "1"
f = chatStoreFile dbPrefix f = chatStoreFile dbPrefix
st <- createChatStore f "myKey" True st <- createChatStore f "myKey" True
Right _ <- withTransaction st $ \db -> runExceptT $ createUser db aliceProfile {preferences = Nothing} True Right _ <- withTransaction st $ \db -> runExceptT $ createUserRecord db (AgentUserId 1) aliceProfile {preferences = Nothing} True
Right cc <- chatMigrateInit dbPrefix "myKey" Right cc <- chatMigrateInit dbPrefix "myKey"
Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "" Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix ""
Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "anotherKey" Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "anotherKey"