diff --git a/changelog.d/1-api-changes/add-get-app-endpoint b/changelog.d/1-api-changes/add-get-app-endpoint index e433e9cc22..f486179017 100644 --- a/changelog.d/1-api-changes/add-get-app-endpoint +++ b/changelog.d/1-api-changes/add-get-app-endpoint @@ -1 +1,8 @@ -Add "get app" endpoint to Brig (`GET /teams/:tid/apps/:id`) \ No newline at end of file +Add "get app" endpoint to Brig (`GET /teams/:tid/apps/:id`) + +Elastic Search re-indexing requires postgres access now. + +You need to do a full re-index of ElasticSearch immediately after the +upgrade. Between server upgrade and a full re-index, old-style bots +(spawned by services) may identify as be listed as regular users in +search results. diff --git a/changelog.d/1-api-changes/add-type-field-to-contact b/changelog.d/1-api-changes/add-type-field-to-contact new file mode 100644 index 0000000000..cb58adb825 --- /dev/null +++ b/changelog.d/1-api-changes/add-type-field-to-contact @@ -0,0 +1 @@ +Add `type` field to search results received from `GET /search/contacts` \ No newline at end of file diff --git a/integration/test/Test/Apps.hs b/integration/test/Test/Apps.hs index 4af7b903b3..95bdf6e4cc 100644 --- a/integration/test/Test/Apps.hs +++ b/integration/test/Test/Apps.hs @@ -88,13 +88,23 @@ testCreateApp = do void $ bindResponse (createApp owner tid new {category = "notinenum"}) $ \resp -> do resp.status `shouldMatchInt` 400 + let foundUserType exactMatchTerm aType = + searchContacts owner exactMatchTerm OwnDomain `bindResponse` \resp -> do + resp.status `shouldMatchInt` 200 + foundDoc <- resp.json %. "documents" >>= asList >>= assertOne + foundDoc %. "type" `shouldMatch` aType + -- App's user is findable from /search/contacts BrigI.refreshIndex OwnDomain - searchContacts owner new.name OwnDomain `bindResponse` \resp -> do - resp.status `shouldMatchInt` 200 - docs <- resp.json %. "documents" >>= asList - foundUids <- for docs objId - foundUids `shouldMatch` [appId] + foundUserType new.name "app" + + -- Owner and regular member still have the type "regular" + memberName <- regularMember %. "name" & asString + foundUserType memberName "regular" + +-- XXX: Why is owner not found? +-- ownerName <- owner %. "name" & asString +-- foundUserType ownerName "regular" testRefreshAppCookie :: (HasCallStack) => App () testRefreshAppCookie = do diff --git a/libs/wire-api/src/Wire/API/User.hs b/libs/wire-api/src/Wire/API/User.hs index 138ded4356..defa322a59 100644 --- a/libs/wire-api/src/Wire/API/User.hs +++ b/libs/wire-api/src/Wire/API/User.hs @@ -472,6 +472,7 @@ instance (1 <= max) => ToJSON (LimitedQualifiedUserIdList max) where data UserType = UserTypeRegular | UserTypeApp | UserTypeBot deriving (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform UserType) + deriving (A.FromJSON, A.ToJSON) via (Schema UserType) instance Default UserType where def = UserTypeRegular @@ -714,34 +715,32 @@ instance FromJSON (EmailVisibility ()) where "visible_to_self" -> pure EmailVisibleToSelf _ -> fail "unexpected value for EmailVisibility settings" -mkUserProfileWithEmail :: Maybe EmailAddress -> User -> UserLegalHoldStatus -> UserProfile -mkUserProfileWithEmail memail u legalHoldStatus = - let ty = case userService u of - Nothing -> UserTypeRegular - Just _ -> UserTypeBot - in -- This profile would be visible to any other user. When a new field is - -- added, please make sure it is OK for other users to have access to it. - UserProfile - { profileQualifiedId = userQualifiedId u, - profileHandle = userHandle u, - profileName = userDisplayName u, - profileTextStatus = userTextStatus u, - profilePict = userPict u, - profileAssets = userAssets u, - profileAccentId = userAccentId u, - profileService = userService u, - profileDeleted = userDeleted u, - profileExpire = userExpire u, - profileTeam = userTeam u, - profileEmail = memail, - profileLegalholdStatus = legalHoldStatus, - profileSupportedProtocols = userSupportedProtocols u, - profileType = ty, - profileSearchable = userSearchable u - } +-- | Create profile, overwriting the email field. Called `mkUserProfile`. +mkUserProfileWithEmail :: Maybe EmailAddress -> UserType -> User -> UserLegalHoldStatus -> UserProfile +mkUserProfileWithEmail memail userType u legalHoldStatus = + -- This profile would be visible to any other user. When a new field is + -- added, please make sure it is OK for other users to have access to it. + UserProfile + { profileQualifiedId = userQualifiedId u, + profileHandle = userHandle u, + profileName = userDisplayName u, + profileTextStatus = userTextStatus u, + profilePict = userPict u, + profileAssets = userAssets u, + profileAccentId = userAccentId u, + profileService = userService u, + profileDeleted = userDeleted u, + profileExpire = userExpire u, + profileTeam = userTeam u, + profileEmail = memail, + profileLegalholdStatus = legalHoldStatus, + profileSupportedProtocols = userSupportedProtocols u, + profileType = userType, + profileSearchable = userSearchable u + } -mkUserProfile :: EmailVisibilityConfigWithViewer -> User -> UserLegalHoldStatus -> UserProfile -mkUserProfile emailVisibilityConfigAndViewer u legalHoldStatus = +mkUserProfile :: EmailVisibilityConfigWithViewer -> UserType -> User -> UserLegalHoldStatus -> UserProfile +mkUserProfile emailVisibilityConfigAndViewer userType u legalHoldStatus = let isEmailVisible = case emailVisibilityConfigAndViewer of EmailVisibleToSelf -> False EmailVisibleIfOnTeam -> isJust (userTeam u) @@ -749,7 +748,7 @@ mkUserProfile emailVisibilityConfigAndViewer u legalHoldStatus = EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership)) -> Just viewerTeamId == userTeam u && TeamMember.hasPermission viewerMembership TeamMember.ViewSameTeamEmails - in mkUserProfileWithEmail (if isEmailVisible then userEmail u else Nothing) u legalHoldStatus + in mkUserProfileWithEmail (if isEmailVisible then userEmail u else Nothing) userType u legalHoldStatus -------------------------------------------------------------------------------- -- NewUser diff --git a/libs/wire-api/src/Wire/API/User/Search.hs b/libs/wire-api/src/Wire/API/User/Search.hs index bddf084994..96e01c24fe 100644 --- a/libs/wire-api/src/Wire/API/User/Search.hs +++ b/libs/wire-api/src/Wire/API/User/Search.hs @@ -42,8 +42,7 @@ import Data.Aeson hiding (object, (.=)) import Data.Aeson qualified as Aeson import Data.Attoparsec.ByteString.Char8 (string) import Data.ByteString.Char8 qualified as C8 -import Data.ByteString.Conversion -import Data.ByteString.Conversion qualified as BS +import Data.ByteString.Conversion as BS import Data.Id (TeamId, UserGroupId, UserId) import Data.Json.Util (UTCTimeMillis) import Data.OpenApi (ToParamSchema (..)) @@ -59,7 +58,7 @@ import Imports import Servant.API (FromHttpApiData, ToHttpApiData (..)) import Web.Internal.HttpApiData (parseQueryParam) import Wire.API.Team.Role (Role) -import Wire.API.User (ManagedBy) +import Wire.API.User (ManagedBy, UserType) import Wire.API.User.Identity (EmailAddress) import Wire.Arbitrary (Arbitrary, GenericUniform (..)) @@ -138,14 +137,15 @@ deriving via (Schema (SearchResult TeamContact)) instance S.ToSchema (SearchResu -------------------------------------------------------------------------------- -- Contact --- | Returned by 'searchIndex' under @/contacts/search@. +-- | Returned by 'searchIndex' under @/search/contacts@. -- This is a subset of 'User' and json instances should reflect that. data Contact = Contact { contactQualifiedId :: Qualified UserId, contactName :: Text, contactColorId :: Maybe Int, contactHandle :: Maybe Text, - contactTeam :: Maybe TeamId + contactTeam :: Maybe TeamId, + contactType :: UserType } deriving stock (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform Contact) @@ -161,6 +161,7 @@ instance ToSchema Contact where <*> contactColorId .= optField "accent_id" (maybeWithDefault Aeson.Null schema) <*> contactHandle .= optField "handle" (maybeWithDefault Aeson.Null schema) <*> contactTeam .= optField "team" (maybeWithDefault Aeson.Null schema) + <*> contactType .= field "type" schema -------------------------------------------------------------------------------- -- TeamContact diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Contact_user.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Contact_user.hs index b3f7c67e29..88b04796f6 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Contact_user.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Contact_user.hs @@ -22,6 +22,7 @@ import Data.Id (Id (Id)) import Data.Qualified (Qualified (Qualified, qDomain, qUnqualified)) import Data.UUID qualified as UUID (fromString) import Imports (Maybe (Just, Nothing), fromJust) +import Wire.API.User import Wire.API.User.Search (Contact (..)) testObject_Contact_user_1 :: Contact @@ -35,7 +36,8 @@ testObject_Contact_user_1 = contactName = "", contactColorId = Just 6, contactHandle = Just "\1089530\NUL|\SO", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } testObject_Contact_user_2 :: Contact @@ -49,7 +51,8 @@ testObject_Contact_user_2 = contactName = "\SYND", contactColorId = Just (-5), contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000002-0000-0008-0000-000400000002"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000002-0000-0008-0000-000400000002"))), + contactType = UserTypeApp } testObject_Contact_user_3 :: Contact @@ -63,7 +66,8 @@ testObject_Contact_user_3 = contactName = "S\1037187D\GS", contactColorId = Just (-4), contactHandle = Just "\175177~\35955c", - contactTeam = Just (Id (fromJust (UUID.fromString "00000006-0000-0005-0000-000700000008"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000006-0000-0005-0000-000700000008"))), + contactType = UserTypeBot } testObject_Contact_user_4 :: Contact @@ -77,7 +81,8 @@ testObject_Contact_user_4 = contactName = "@=\ETX", contactColorId = Nothing, contactHandle = Just "6", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000500000004"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000500000004"))), + contactType = UserTypeRegular } testObject_Contact_user_5 :: Contact @@ -91,7 +96,8 @@ testObject_Contact_user_5 = contactName = "5m~\DC4`", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } testObject_Contact_user_6 :: Contact @@ -105,7 +111,8 @@ testObject_Contact_user_6 = contactName = "Cst\995547U", contactColorId = Nothing, contactHandle = Just "qI", - contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0004-0000-000600000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0004-0000-000600000000"))), + contactType = UserTypeRegular } testObject_Contact_user_7 :: Contact @@ -119,7 +126,8 @@ testObject_Contact_user_7 = contactName = "\b74\ENQ", contactColorId = Just 5, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000008-0000-0001-0000-000400000008"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000008-0000-0001-0000-000400000008"))), + contactType = UserTypeRegular } testObject_Contact_user_8 :: Contact @@ -133,7 +141,8 @@ testObject_Contact_user_8 = contactName = "w\1050194\993461#\\", contactColorId = Just (-2), contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0007-0000-000500000002"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0007-0000-000500000002"))), + contactType = UserTypeRegular } testObject_Contact_user_9 :: Contact @@ -147,7 +156,8 @@ testObject_Contact_user_9 = contactName = ",\1041199 \v\1077257", contactColorId = Just 5, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0002-0000-000500000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0002-0000-000500000000"))), + contactType = UserTypeRegular } testObject_Contact_user_10 :: Contact @@ -161,7 +171,8 @@ testObject_Contact_user_10 = contactName = "(\1103086\1105553H/", contactColorId = Just 0, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0006-0000-000700000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0006-0000-000700000000"))), + contactType = UserTypeRegular } testObject_Contact_user_11 :: Contact @@ -175,7 +186,8 @@ testObject_Contact_user_11 = contactName = "+\DC4\1063683<", contactColorId = Just 6, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000007-0000-0008-0000-000600000004"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000007-0000-0008-0000-000600000004"))), + contactType = UserTypeRegular } testObject_Contact_user_12 :: Contact @@ -189,7 +201,8 @@ testObject_Contact_user_12 = contactName = "l\DC1\ETB`\ETX", contactColorId = Just (-4), contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } testObject_Contact_user_13 :: Contact @@ -203,7 +216,8 @@ testObject_Contact_user_13 = contactName = "\SYN\1030541\v8z", contactColorId = Just (-3), contactHandle = Just "E\EM\US[58", - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0003-0000-000000000005"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0003-0000-000000000005"))), + contactType = UserTypeRegular } testObject_Contact_user_14 :: Contact @@ -217,7 +231,8 @@ testObject_Contact_user_14 = contactName = "7", contactColorId = Just (-2), contactHandle = Just "h\CAN", - contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0008-0000-000700000008"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000005-0000-0008-0000-000700000008"))), + contactType = UserTypeRegular } testObject_Contact_user_15 :: Contact @@ -231,7 +246,8 @@ testObject_Contact_user_15 = contactName = "U6\ESC*\SO", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000006-0000-0006-0000-000800000006"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000006-0000-0006-0000-000800000006"))), + contactType = UserTypeRegular } testObject_Contact_user_16 :: Contact @@ -245,7 +261,8 @@ testObject_Contact_user_16 = contactName = "l", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0006-0000-000200000007"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0006-0000-000200000007"))), + contactType = UserTypeRegular } testObject_Contact_user_17 :: Contact @@ -259,7 +276,8 @@ testObject_Contact_user_17 = contactName = "fI\8868\&3z", contactColorId = Nothing, contactHandle = Just "3", - contactTeam = Just (Id (fromJust (UUID.fromString "00000004-0000-0007-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000004-0000-0007-0000-000000000001"))), + contactType = UserTypeRegular } testObject_Contact_user_18 :: Contact @@ -273,7 +291,8 @@ testObject_Contact_user_18 = contactName = "\"jC\74801\144577\DC2", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000000000007"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000000000007"))), + contactType = UserTypeRegular } testObject_Contact_user_19 :: Contact @@ -287,7 +306,8 @@ testObject_Contact_user_19 = contactName = "I", contactColorId = Just (-1), contactHandle = Just "\"7\ACK!", - contactTeam = Just (Id (fromJust (UUID.fromString "00000006-0000-0004-0000-000000000003"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000006-0000-0004-0000-000000000003"))), + contactType = UserTypeRegular } testObject_Contact_user_20 :: Contact @@ -301,5 +321,6 @@ testObject_Contact_user_20 = contactName = "|K\n\n\t", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SearchResult_20Contact_user.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SearchResult_20Contact_user.hs index bb79681738..1b9dc63b00 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SearchResult_20Contact_user.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SearchResult_20Contact_user.hs @@ -24,6 +24,7 @@ import Data.Id (Id (Id)) import Data.Qualified (Qualified (Qualified, qDomain, qUnqualified)) import Data.UUID qualified as UUID (fromString) import Imports (Bool (..), Maybe (Just, Nothing), fromJust) +import Wire.API.User (UserType (UserTypeRegular)) import Wire.API.User.Search (Contact (..), FederatedUserSearchPolicy (ExactHandleSearch, FullSearch), PagingState (..), SearchResult (..)) testObject_SearchResult_20Contact_user_1 :: SearchResult Contact @@ -66,7 +67,8 @@ testObject_SearchResult_20Contact_user_3 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + contactType = UserTypeRegular } ], searchPolicy = FullSearch, @@ -90,7 +92,8 @@ testObject_SearchResult_20Contact_user_4 = contactName = "", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -101,7 +104,8 @@ testObject_SearchResult_20Contact_user_4 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -112,7 +116,8 @@ testObject_SearchResult_20Contact_user_4 = contactName = "", contactColorId = Just 0, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -123,7 +128,8 @@ testObject_SearchResult_20Contact_user_4 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -134,7 +140,8 @@ testObject_SearchResult_20Contact_user_4 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -145,7 +152,8 @@ testObject_SearchResult_20Contact_user_4 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), + contactType = UserTypeRegular } ], searchPolicy = FullSearch, @@ -169,7 +177,8 @@ testObject_SearchResult_20Contact_user_5 = contactName = "z", contactColorId = Just 1, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), + contactType = UserTypeRegular } ], searchPolicy = FullSearch, @@ -205,7 +214,8 @@ testObject_SearchResult_20Contact_user_7 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -216,7 +226,8 @@ testObject_SearchResult_20Contact_user_7 = contactName = "", contactColorId = Just 0, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + contactType = UserTypeRegular } ], searchPolicy = FullSearch, @@ -240,7 +251,8 @@ testObject_SearchResult_20Contact_user_8 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + contactType = UserTypeRegular } ], searchPolicy = FullSearch, @@ -288,7 +300,8 @@ testObject_SearchResult_20Contact_user_11 = contactName = "", contactColorId = Just 0, contactHandle = Nothing, - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -299,7 +312,8 @@ testObject_SearchResult_20Contact_user_11 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } ], searchPolicy = ExactHandleSearch, @@ -335,7 +349,8 @@ testObject_SearchResult_20Contact_user_13 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -346,7 +361,8 @@ testObject_SearchResult_20Contact_user_13 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -357,7 +373,8 @@ testObject_SearchResult_20Contact_user_13 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -368,7 +385,8 @@ testObject_SearchResult_20Contact_user_13 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } ], searchPolicy = ExactHandleSearch, @@ -392,7 +410,8 @@ testObject_SearchResult_20Contact_user_14 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -403,7 +422,8 @@ testObject_SearchResult_20Contact_user_14 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } ], searchPolicy = ExactHandleSearch, @@ -475,7 +495,8 @@ testObject_SearchResult_20Contact_user_19 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -486,7 +507,8 @@ testObject_SearchResult_20Contact_user_19 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), + contactType = UserTypeRegular } ], searchPolicy = ExactHandleSearch, @@ -510,7 +532,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -521,7 +544,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -532,7 +556,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -543,7 +568,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -554,7 +580,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -565,7 +592,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -576,7 +604,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -587,7 +616,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -598,7 +628,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Nothing, contactHandle = Just "", - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -609,7 +640,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -620,7 +652,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -631,7 +664,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Just "", - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), + contactType = UserTypeRegular }, Contact { contactQualifiedId = @@ -642,7 +676,8 @@ testObject_SearchResult_20Contact_user_20 = contactName = "", contactColorId = Just 0, contactHandle = Nothing, - contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))) + contactTeam = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + contactType = UserTypeRegular } ], searchPolicy = ExactHandleSearch, diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/Contact.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/Contact.hs index 513f6d30af..3b2bc6fed9 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/Contact.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/Contact.hs @@ -22,6 +22,7 @@ import Data.Id (Id (Id)) import Data.Qualified (Qualified (Qualified)) import Data.UUID qualified as UUID import Imports +import Wire.API.User (UserType (UserTypeRegular)) import Wire.API.User.Search (Contact (..)) testObject_Contact_1 :: Contact @@ -31,7 +32,8 @@ testObject_Contact_1 = contactName = "Foobar", contactColorId = Just 1, contactHandle = Just "foobar1", - contactTeam = Just $ Id (fromJust (UUID.fromString "00000018-0000-0020-0000-000e00000002")) + contactTeam = Just $ Id (fromJust (UUID.fromString "00000018-0000-0020-0000-000e00000002")), + contactType = UserTypeRegular } testObject_Contact_2 :: Contact @@ -41,5 +43,6 @@ testObject_Contact_2 = contactName = "Foobar2", contactColorId = Nothing, contactHandle = Nothing, - contactTeam = Nothing + contactTeam = Nothing, + contactType = UserTypeRegular } diff --git a/libs/wire-api/test/golden/testObject_Contact_1.json b/libs/wire-api/test/golden/testObject_Contact_1.json index fb1bdac6dd..01906d05a2 100644 --- a/libs/wire-api/test/golden/testObject_Contact_1.json +++ b/libs/wire-api/test/golden/testObject_Contact_1.json @@ -7,5 +7,6 @@ "domain": "example.com", "id": "00000018-0000-0020-0000-000e00000002" }, - "team": "00000018-0000-0020-0000-000e00000002" + "team": "00000018-0000-0020-0000-000e00000002", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_2.json b/libs/wire-api/test/golden/testObject_Contact_2.json index aca2362249..a7171d11e6 100644 --- a/libs/wire-api/test/golden/testObject_Contact_2.json +++ b/libs/wire-api/test/golden/testObject_Contact_2.json @@ -7,5 +7,6 @@ "domain": "another.example.com", "id": "00000018-0000-0020-0000-000e00000003" }, - "team": null + "team": null, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_1.json b/libs/wire-api/test/golden/testObject_Contact_user_1.json index 825287dc78..44a3c28869 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_1.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_1.json @@ -7,5 +7,6 @@ "domain": "j00.8y.yr3isy2m", "id": "00000007-0000-0003-0000-000300000005" }, - "team": null + "team": null, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_10.json b/libs/wire-api/test/golden/testObject_Contact_user_10.json index 7618387345..57c4e71fa2 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_10.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_10.json @@ -7,5 +7,6 @@ "domain": "avs-82k0.quv1k-5", "id": "00000000-0000-0000-0000-000800000007" }, - "team": "00000005-0000-0006-0000-000700000000" + "team": "00000005-0000-0006-0000-000700000000", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_11.json b/libs/wire-api/test/golden/testObject_Contact_user_11.json index f6831f331b..dfd13cc9a2 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_11.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_11.json @@ -7,5 +7,6 @@ "domain": "156y.t.qxp-y26x", "id": "00000002-0000-0005-0000-000700000004" }, - "team": "00000007-0000-0008-0000-000600000004" + "team": "00000007-0000-0008-0000-000600000004", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_12.json b/libs/wire-api/test/golden/testObject_Contact_user_12.json index 38ca139c60..0c504b2229 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_12.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_12.json @@ -7,5 +7,6 @@ "domain": "d2wnzbn.8.k2d4-103", "id": "00000004-0000-0002-0000-000300000003" }, - "team": null + "team": null, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_13.json b/libs/wire-api/test/golden/testObject_Contact_user_13.json index 3ee4544fc4..81835b8a16 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_13.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_13.json @@ -7,5 +7,6 @@ "domain": "902cigj.v2t56", "id": "00000002-0000-0006-0000-000800000006" }, - "team": "00000001-0000-0003-0000-000000000005" + "team": "00000001-0000-0003-0000-000000000005", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_14.json b/libs/wire-api/test/golden/testObject_Contact_user_14.json index 876b00ee25..14ef9ebd56 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_14.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_14.json @@ -7,5 +7,6 @@ "domain": "6z.ml.80ps6j5r.l", "id": "00000000-0000-0003-0000-000300000006" }, - "team": "00000005-0000-0008-0000-000700000008" + "team": "00000005-0000-0008-0000-000700000008", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_15.json b/libs/wire-api/test/golden/testObject_Contact_user_15.json index 5664eab786..619c62f22a 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_15.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_15.json @@ -7,5 +7,6 @@ "domain": "739.e-h8g", "id": "00000002-0000-0000-0000-000200000002" }, - "team": "00000006-0000-0006-0000-000800000006" + "team": "00000006-0000-0006-0000-000800000006", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_16.json b/libs/wire-api/test/golden/testObject_Contact_user_16.json index a04c2bf386..1ba59c042a 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_16.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_16.json @@ -7,5 +7,6 @@ "domain": "t82.x5i8-i", "id": "00000000-0000-0006-0000-000500000006" }, - "team": "00000000-0000-0006-0000-000200000007" + "team": "00000000-0000-0006-0000-000200000007", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_17.json b/libs/wire-api/test/golden/testObject_Contact_user_17.json index fd68c31c3a..54bb4c0fab 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_17.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_17.json @@ -7,5 +7,6 @@ "domain": "o5b0hrjp3x0b96.v1gxp3", "id": "00000003-0000-0008-0000-000700000002" }, - "team": "00000004-0000-0007-0000-000000000001" + "team": "00000004-0000-0007-0000-000000000001", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_18.json b/libs/wire-api/test/golden/testObject_Contact_user_18.json index b5f25e02fc..5c2357f91b 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_18.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_18.json @@ -7,5 +7,6 @@ "domain": "72n2x7x0.ztb0s51", "id": "00000004-0000-0006-0000-000800000006" }, - "team": "00000001-0000-0002-0000-000000000007" + "team": "00000001-0000-0002-0000-000000000007", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_19.json b/libs/wire-api/test/golden/testObject_Contact_user_19.json index 7cf17bc4f5..799bff0386 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_19.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_19.json @@ -7,5 +7,6 @@ "domain": "h664l.dio6", "id": "00000005-0000-0003-0000-000700000007" }, - "team": "00000006-0000-0004-0000-000000000003" + "team": "00000006-0000-0004-0000-000000000003", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_2.json b/libs/wire-api/test/golden/testObject_Contact_user_2.json index 4ad15e595a..523e300330 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_2.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_2.json @@ -7,5 +7,6 @@ "domain": "z.l--66-i8g8a9", "id": "00000006-0000-0004-0000-000100000007" }, - "team": "00000002-0000-0008-0000-000400000002" + "team": "00000002-0000-0008-0000-000400000002", + "type": "app" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_20.json b/libs/wire-api/test/golden/testObject_Contact_user_20.json index 079817785a..0021c4f0b6 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_20.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_20.json @@ -7,5 +7,6 @@ "domain": "pam223.b6", "id": "00000000-0000-0000-0000-000500000001" }, - "team": null + "team": null, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_3.json b/libs/wire-api/test/golden/testObject_Contact_user_3.json index e8cb5a2eee..d5a5f47407 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_3.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_3.json @@ -7,5 +7,6 @@ "domain": "h.y-2k71.rh", "id": "00000005-0000-0003-0000-000700000003" }, - "team": "00000006-0000-0005-0000-000700000008" + "team": "00000006-0000-0005-0000-000700000008", + "type": "bot" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_4.json b/libs/wire-api/test/golden/testObject_Contact_user_4.json index 13b1b0c89a..c9f77f4eaa 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_4.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_4.json @@ -7,5 +7,6 @@ "domain": "2347.cye2i7.sn.r2z83.d03", "id": "00000003-0000-0002-0000-000000000004" }, - "team": "00000000-0000-0000-0000-000500000004" + "team": "00000000-0000-0000-0000-000500000004", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_5.json b/libs/wire-api/test/golden/testObject_Contact_user_5.json index 4442242a90..ac359467ef 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_5.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_5.json @@ -7,5 +7,6 @@ "domain": "v0u29n3.er", "id": "00000004-0000-0000-0000-000300000005" }, - "team": null + "team": null, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_6.json b/libs/wire-api/test/golden/testObject_Contact_user_6.json index 9ca25db0d5..c5a1adbc52 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_6.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_6.json @@ -7,5 +7,6 @@ "domain": "6k.p", "id": "00000003-0000-0001-0000-000400000000" }, - "team": "00000005-0000-0004-0000-000600000000" + "team": "00000005-0000-0004-0000-000600000000", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_7.json b/libs/wire-api/test/golden/testObject_Contact_user_7.json index df0499cc7e..093a117d6f 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_7.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_7.json @@ -7,5 +7,6 @@ "domain": "yr.e1-d", "id": "00000001-0000-0002-0000-000800000008" }, - "team": "00000008-0000-0001-0000-000400000008" + "team": "00000008-0000-0001-0000-000400000008", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_8.json b/libs/wire-api/test/golden/testObject_Contact_user_8.json index 67ad84dfe7..be0997d3be 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_8.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_8.json @@ -7,5 +7,6 @@ "domain": "51r9in-k6i5l8-7y6.t205p-gl2", "id": "00000002-0000-0002-0000-000600000008" }, - "team": "00000001-0000-0007-0000-000500000002" + "team": "00000001-0000-0007-0000-000500000002", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_Contact_user_9.json b/libs/wire-api/test/golden/testObject_Contact_user_9.json index 9318c77233..862db1f6de 100644 --- a/libs/wire-api/test/golden/testObject_Contact_user_9.json +++ b/libs/wire-api/test/golden/testObject_Contact_user_9.json @@ -7,5 +7,6 @@ "domain": "37-p6v67.g", "id": "00000000-0000-0000-0000-000600000008" }, - "team": "00000005-0000-0002-0000-000500000000" + "team": "00000005-0000-0002-0000-000500000000", + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_SearchResultContact_1.json b/libs/wire-api/test/golden/testObject_SearchResultContact_1.json index 3dfe76acce..78b27a72f0 100644 --- a/libs/wire-api/test/golden/testObject_SearchResultContact_1.json +++ b/libs/wire-api/test/golden/testObject_SearchResultContact_1.json @@ -9,7 +9,8 @@ "domain": "example.com", "id": "00000018-0000-0020-0000-000e00000002" }, - "team": "00000018-0000-0020-0000-000e00000002" + "team": "00000018-0000-0020-0000-000e00000002", + "type": "regular" }, { "accent_id": null, @@ -20,7 +21,8 @@ "domain": "another.example.com", "id": "00000018-0000-0020-0000-000e00000003" }, - "team": null + "team": null, + "type": "regular" } ], "found": 2, diff --git a/libs/wire-api/test/golden/testObject_SearchResultContact_2.json b/libs/wire-api/test/golden/testObject_SearchResultContact_2.json index 01117c7003..875534f79a 100644 --- a/libs/wire-api/test/golden/testObject_SearchResultContact_2.json +++ b/libs/wire-api/test/golden/testObject_SearchResultContact_2.json @@ -9,7 +9,8 @@ "domain": "example.com", "id": "00000018-0000-0020-0000-000e00000002" }, - "team": "00000018-0000-0020-0000-000e00000002" + "team": "00000018-0000-0020-0000-000e00000002", + "type": "regular" }, { "accent_id": null, @@ -20,7 +21,8 @@ "domain": "another.example.com", "id": "00000018-0000-0020-0000-000e00000003" }, - "team": null + "team": null, + "type": "regular" } ], "found": 2, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_11.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_11.json index e373317287..b05e65f929 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_11.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_11.json @@ -9,7 +9,8 @@ "domain": "bza.j", "id": "00000000-0000-0000-0000-000000000001" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": 0, @@ -20,7 +21,8 @@ "domain": "zwv.u6-f", "id": "00000001-0000-0000-0000-000000000001" }, - "team": null + "team": null, + "type": "regular" } ], "found": -1, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_13.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_13.json index 01c6c1eee8..1148666947 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_13.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_13.json @@ -9,7 +9,8 @@ "domain": "795n1zf6-he8-97ur4w.o7r---053", "id": "00000000-0000-0001-0000-000000000000" }, - "team": "00000000-0000-0000-0000-000100000000" + "team": "00000000-0000-0000-0000-000100000000", + "type": "regular" }, { "accent_id": 0, @@ -20,7 +21,8 @@ "domain": "v-t6qc.e.so7jqwv", "id": "00000000-0000-0001-0000-000000000000" }, - "team": "00000000-0000-0001-0000-000000000001" + "team": "00000000-0000-0001-0000-000000000001", + "type": "regular" }, { "accent_id": 0, @@ -31,7 +33,8 @@ "domain": "335.a3.p49c--e-fjz337", "id": "00000000-0000-0000-0000-000100000001" }, - "team": "00000000-0000-0001-0000-000000000001" + "team": "00000000-0000-0001-0000-000000000001", + "type": "regular" }, { "accent_id": null, @@ -42,7 +45,8 @@ "domain": "g.g3n", "id": "00000001-0000-0000-0000-000000000000" }, - "team": null + "team": null, + "type": "regular" } ], "found": 3, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_14.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_14.json index 7e80395ae6..daf4f793c8 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_14.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_14.json @@ -9,7 +9,8 @@ "domain": "c00y0ks9-6.q", "id": "00000000-0000-0001-0000-000100000000" }, - "team": "00000000-0000-0000-0000-000000000000" + "team": "00000000-0000-0000-0000-000000000000", + "type": "regular" }, { "accent_id": null, @@ -20,7 +21,8 @@ "domain": "g.44.s3dq77", "id": "00000001-0000-0001-0000-000000000001" }, - "team": null + "team": null, + "type": "regular" } ], "found": 1, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_19.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_19.json index c34194c529..2c656608d4 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_19.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_19.json @@ -9,7 +9,8 @@ "domain": "5de.v-6", "id": "00000000-0000-0001-0000-000100000001" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": null, @@ -20,7 +21,8 @@ "domain": "z76.kcuxql-9", "id": "00000000-0000-0001-0000-000100000000" }, - "team": "00000000-0000-0000-0000-000000000001" + "team": "00000000-0000-0000-0000-000000000001", + "type": "regular" } ], "found": 4, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_20.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_20.json index 471a9a11c8..f5fe22a975 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_20.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_20.json @@ -9,7 +9,8 @@ "domain": "66h.j", "id": "00000000-0000-0001-0000-000000000000" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": 0, @@ -20,7 +21,8 @@ "domain": "7s.k881-q-42", "id": "00000000-0000-0000-0000-000100000000" }, - "team": "00000000-0000-0000-0000-000100000001" + "team": "00000000-0000-0000-0000-000100000001", + "type": "regular" }, { "accent_id": 0, @@ -31,7 +33,8 @@ "domain": "1ux.dy", "id": "00000001-0000-0001-0000-000100000001" }, - "team": "00000000-0000-0001-0000-000000000001" + "team": "00000000-0000-0001-0000-000000000001", + "type": "regular" }, { "accent_id": null, @@ -42,7 +45,8 @@ "domain": "o.xi", "id": "00000001-0000-0000-0000-000000000000" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": null, @@ -53,7 +57,8 @@ "domain": "x5c.v", "id": "00000000-0000-0001-0000-000100000000" }, - "team": "00000001-0000-0000-0000-000000000000" + "team": "00000001-0000-0000-0000-000000000000", + "type": "regular" }, { "accent_id": 0, @@ -64,7 +69,8 @@ "domain": "9p-8z5.i", "id": "00000001-0000-0000-0000-000100000001" }, - "team": "00000001-0000-0001-0000-000000000001" + "team": "00000001-0000-0001-0000-000000000001", + "type": "regular" }, { "accent_id": 0, @@ -75,7 +81,8 @@ "domain": "h1t7.9.j492", "id": "00000000-0000-0000-0000-000100000000" }, - "team": "00000000-0000-0000-0000-000000000001" + "team": "00000000-0000-0000-0000-000000000001", + "type": "regular" }, { "accent_id": 0, @@ -86,7 +93,8 @@ "domain": "p9.y", "id": "00000000-0000-0000-0000-000100000001" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": null, @@ -97,7 +105,8 @@ "domain": "saz.d0v8", "id": "00000000-0000-0000-0000-000100000001" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": 0, @@ -108,7 +117,8 @@ "domain": "gpz.28--u.1646.v5", "id": "00000000-0000-0001-0000-000000000000" }, - "team": "00000000-0000-0001-0000-000100000001" + "team": "00000000-0000-0001-0000-000100000001", + "type": "regular" }, { "accent_id": 0, @@ -119,7 +129,8 @@ "domain": "8p.5.x11-s", "id": "00000001-0000-0000-0000-000100000001" }, - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "type": "regular" }, { "accent_id": 0, @@ -130,7 +141,8 @@ "domain": "q4x5z.mwi3", "id": "00000000-0000-0001-0000-000000000001" }, - "team": "00000000-0000-0001-0000-000000000000" + "team": "00000000-0000-0001-0000-000000000000", + "type": "regular" }, { "accent_id": 0, @@ -141,7 +153,8 @@ "domain": "38.b7", "id": "00000000-0000-0000-0000-000000000000" }, - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "type": "regular" } ], "found": 7, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_3.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_3.json index d3a743390b..eb3ce536ab 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_3.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_3.json @@ -9,7 +9,8 @@ "domain": "guh.e", "id": "00000001-0000-0001-0000-000100000000" }, - "team": "00000000-0000-0000-0000-000100000000" + "team": "00000000-0000-0000-0000-000100000000", + "type": "regular" } ], "found": 4, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_4.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_4.json index b635210594..5118eee5c3 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_4.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_4.json @@ -9,7 +9,8 @@ "domain": "2.60--1n1.ds", "id": "00000000-0000-0000-0000-000000000000" }, - "team": "00000001-0000-0000-0000-000100000001" + "team": "00000001-0000-0000-0000-000100000001", + "type": "regular" }, { "accent_id": null, @@ -20,7 +21,8 @@ "domain": "onrg.u", "id": "00000000-0000-0000-0000-000100000001" }, - "team": "00000000-0000-0000-0000-000100000000" + "team": "00000000-0000-0000-0000-000100000000", + "type": "regular" }, { "accent_id": 0, @@ -31,7 +33,8 @@ "domain": "660.v1.8z2.a-4dv.y", "id": "00000001-0000-0000-0000-000000000000" }, - "team": "00000000-0000-0001-0000-000100000001" + "team": "00000000-0000-0001-0000-000100000001", + "type": "regular" }, { "accent_id": null, @@ -42,7 +45,8 @@ "domain": "t102d9m3.tb-dryc9.ws300w5xc4", "id": "00000001-0000-0001-0000-000000000001" }, - "team": null + "team": null, + "type": "regular" }, { "accent_id": 0, @@ -53,7 +57,8 @@ "domain": "54up.l8h-b-g-i.x-c.9-7.we35781l0b", "id": "00000000-0000-0000-0000-000100000000" }, - "team": "00000000-0000-0001-0000-000000000000" + "team": "00000000-0000-0001-0000-000000000000", + "type": "regular" }, { "accent_id": 0, @@ -64,7 +69,8 @@ "domain": "a.h9-1", "id": "00000000-0000-0001-0000-000100000000" }, - "team": "00000000-0000-0000-0000-000000000000" + "team": "00000000-0000-0000-0000-000000000000", + "type": "regular" } ], "found": -5, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_5.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_5.json index c958b20589..318d927d81 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_5.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_5.json @@ -9,7 +9,8 @@ "domain": "1b-y90e265f.l-c", "id": "00000001-0000-0000-0000-000000000001" }, - "team": "00000001-0000-0000-0000-000100000001" + "team": "00000001-0000-0000-0000-000100000001", + "type": "regular" } ], "found": -6, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_7.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_7.json index 4ec4137a7f..a978842e1b 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_7.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_7.json @@ -9,7 +9,8 @@ "domain": "1386---3-nddry.o", "id": "00000001-0000-0000-0000-000000000000" }, - "team": "00000001-0000-0000-0000-000000000001" + "team": "00000001-0000-0000-0000-000000000001", + "type": "regular" }, { "accent_id": 0, @@ -20,7 +21,8 @@ "domain": "j-cz923pu.l6.73-6.qq05n.4ig.dl3", "id": "00000001-0000-0000-0000-000100000001" }, - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "type": "regular" } ], "found": 7, diff --git a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_8.json b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_8.json index e2881a4de6..302b38265d 100644 --- a/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_8.json +++ b/libs/wire-api/test/golden/testObject_SearchResult_20Contact_user_8.json @@ -9,7 +9,8 @@ "domain": "6n.n08ejr-a", "id": "00000001-0000-0001-0000-000100000001" }, - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "type": "regular" } ], "found": -7, diff --git a/libs/wire-api/test/unit/Test/Wire/API/User.hs b/libs/wire-api/test/unit/Test/Wire/API/User.hs index 11ce3d914e..684a5f9fb8 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/User.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/User.hs @@ -63,7 +63,7 @@ testEmailVisibleToSelf :: TestTree testEmailVisibleToSelf = testProperty "should not contain email when email visibility is EmailVisibleToSelf" $ \user lhStatus -> - let profile = mkUserProfile EmailVisibleToSelf user lhStatus + let profile = mkUserProfile EmailVisibleToSelf UserTypeRegular user lhStatus in profileEmail profile === Nothing .&&. profileLegalholdStatus profile === lhStatus @@ -71,7 +71,7 @@ testEmailVisibleIfOnTeam :: TestTree testEmailVisibleIfOnTeam = testProperty "should contain email only if the user has one and is part of a team when email visibility is EmailVisibleIfOnTeam" $ \user lhStatus -> - let profile = mkUserProfile EmailVisibleIfOnTeam user lhStatus + let profile = mkUserProfile EmailVisibleIfOnTeam UserTypeRegular user lhStatus in (profileEmail profile === (userTeam user *> userEmail user)) .&&. profileLegalholdStatus profile === lhStatus @@ -81,13 +81,13 @@ testEmailVisibleIfOnSameTeam = where testNoViewerTeam = testProperty "should not contain email when viewer is not part of a team" $ \user lhStatus -> - let profile = mkUserProfile (EmailVisibleIfOnSameTeam Nothing) user lhStatus + let profile = mkUserProfile (EmailVisibleIfOnSameTeam Nothing) UserTypeRegular user lhStatus in (profileEmail profile === Nothing) .&&. profileLegalholdStatus profile === lhStatus testViewerDifferentTeam = testProperty "should not contain email when viewer is not part of the same team" $ \viewerTeamId viewerMembership user lhStatus -> - let profile = mkUserProfile (EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership))) user lhStatus + let profile = mkUserProfile (EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership))) UserTypeRegular user lhStatus in Just viewerTeamId /= userTeam user ==> ( profileEmail profile === Nothing .&&. profileLegalholdStatus profile === lhStatus @@ -97,7 +97,7 @@ testEmailVisibleIfOnSameTeam = \viewerTeamId (viewerMembershipNoRole :: TeamMember) userNoTeam lhStatus -> let user = userNoTeam {userTeam = Just viewerTeamId} viewerMembership = viewerMembershipNoRole & TeamMember.permissions .~ TeamMember.rolePermissions RoleExternalPartner - profile = mkUserProfile (EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership))) user lhStatus + profile = mkUserProfile (EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership))) UserTypeRegular user lhStatus in ( profileEmail profile === Nothing .&&. profileLegalholdStatus profile === lhStatus ) @@ -106,7 +106,7 @@ testEmailVisibleIfOnSameTeam = \viewerTeamId (viewerMembershipNoRole :: TeamMember) viewerRole userNoTeam lhStatus -> let user = userNoTeam {userTeam = Just viewerTeamId} viewerMembership = viewerMembershipNoRole & TeamMember.permissions .~ TeamMember.rolePermissions viewerRole - profile = mkUserProfile (EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership))) user lhStatus + profile = mkUserProfile (EmailVisibleIfOnSameTeam (Just (viewerTeamId, viewerMembership))) UserTypeRegular user lhStatus in viewerRole /= RoleExternalPartner ==> ( profileEmail profile === userEmail user .&&. profileLegalholdStatus profile === lhStatus diff --git a/libs/wire-subsystems/src/Wire/IndexedUserStore/Bulk/ElasticSearch.hs b/libs/wire-subsystems/src/Wire/IndexedUserStore/Bulk/ElasticSearch.hs index 84c137d9c3..6bc00795fc 100644 --- a/libs/wire-subsystems/src/Wire/IndexedUserStore/Bulk/ElasticSearch.hs +++ b/libs/wire-subsystems/src/Wire/IndexedUserStore/Bulk/ElasticSearch.hs @@ -34,6 +34,8 @@ import System.Logger.Message qualified as Log import Wire.API.Team.Feature import Wire.API.Team.Member.Info import Wire.API.Team.Role +import Wire.API.User +import Wire.AppStore import Wire.GalleyAPIAccess import Wire.IndexedUserStore (IndexedUserStore) import Wire.IndexedUserStore qualified as IndexedUserStore @@ -49,6 +51,7 @@ import Wire.UserStore.IndexUser interpretIndexedUserStoreBulk :: ( Member TinyLog r, Member UserStore r, + Member AppStore r, Member (Concurrency Unsafe) r, Member GalleyAPIAccess r, Member IndexedUserStore r, @@ -64,6 +67,7 @@ interpretIndexedUserStoreBulk = interpret \case syncAllUsersImpl :: forall r. ( Member UserStore r, + Member AppStore r, Member TinyLog r, Member (Concurrency 'Unsafe) r, Member GalleyAPIAccess r, @@ -75,6 +79,7 @@ syncAllUsersImpl = syncAllUsersWithVersion ES.ExternalGT forceSyncAllUsersImpl :: forall r. ( Member UserStore r, + Member AppStore r, Member TinyLog r, Member (Concurrency 'Unsafe) r, Member GalleyAPIAccess r, @@ -86,6 +91,7 @@ forceSyncAllUsersImpl = syncAllUsersWithVersion ES.ExternalGTE syncAllUsersWithVersion :: forall r. ( Member UserStore r, + Member AppStore r, Member TinyLog r, Member (Concurrency 'Unsafe) r, Member GalleyAPIAccess r, @@ -108,15 +114,26 @@ syncAllUsersWithVersion mkVersion = mkUserDocs :: ConduitT [IndexUser] [(ES.DocId, UserDoc, ES.VersionControl)] (Sem r) () mkUserDocs = Conduit.mapM $ \page -> do + -- TODO: extract team visibilities, roles and user type more efficiently sending one query per page + -- TODO: introduce type ExtendedUser (or something), which + -- contains User, Maybe Role, UserType, ..., and pass around + -- ExtendedUser. this should make the code less convoluted. let teams :: Map TeamId [IndexUser] = Map.fromListWith (<>) $ mapMaybe (\u -> (,[u]) . value <$> u.teamId) page teamIds = Map.keys teams visMap <- fmap Map.fromList . unsafePooledForConcurrentlyN 16 teamIds $ \t -> (t,) <$> teamSearchVisibilityInbound t + userTypes :: Map UserId UserType <- fmap Map.fromList . unsafePooledForConcurrentlyN 16 page $ \iu -> + (iu.userId,) <$> getUserType iu roles :: Map UserId (WithWritetime Role) <- fmap (Map.fromList . concat) . unsafePooledForConcurrentlyN 16 (Map.toList teams) $ \(t, us) -> do tms <- (.members) <$> selectTeamMemberInfos t (fmap (.userId) us) pure $ mapMaybe mkRoleWithWriteTime tms let vis indexUser = fromMaybe defaultSearchVisibilityInbound $ (flip Map.lookup visMap . value =<< indexUser.teamId) - mkUserDoc indexUser = indexUserToDoc (vis indexUser) ((.value) <$> Map.lookup indexUser.userId roles) indexUser + mkUserDoc indexUser = + indexUserToDoc + (vis indexUser) + (fromMaybe (error "impossible") (Map.lookup indexUser.userId userTypes)) + ((.value) <$> Map.lookup indexUser.userId roles) + indexUser mkDocVersion u = mkVersion . ES.ExternalDocVersion . docVersion $ indexUserToVersion (Map.lookup u.userId roles) u pure $ map (\u -> (userIdToDocId u.userId, mkUserDoc u, mkDocVersion u)) page @@ -137,6 +154,7 @@ migrateDataImpl :: Member (Error MigrationException) r, Member IndexedUserMigrationStore r, Member UserStore r, + Member AppStore r, Member (Concurrency Unsafe) r, Member GalleyAPIAccess r, Member TinyLog r @@ -165,3 +183,17 @@ teamSearchVisibilityInbound :: (Member GalleyAPIAccess r) => TeamId -> Sem r Sea teamSearchVisibilityInbound tid = searchVisibilityInboundFromFeatureStatus . (.status) <$> getFeatureConfigForTeam @_ @SearchVisibilityInboundConfig tid + +-- | TODO: this is duplicated code from UserSubsystem, we should probably expose it as an action there. +getUserType :: + forall r. + (Member AppStore r) => + IndexUser -> + Sem r UserType +getUserType iu = case iu.serviceId of + Just _ -> pure UserTypeBot + Nothing -> do + mmApp <- mapM (getApp iu.userId) (iu.teamId <&> (.value)) + case join mmApp of + Just _ -> pure UserTypeApp + Nothing -> pure UserTypeRegular diff --git a/libs/wire-subsystems/src/Wire/UserSearch/Types.hs b/libs/wire-subsystems/src/Wire/UserSearch/Types.hs index 317a309def..f328271455 100644 --- a/libs/wire-subsystems/src/Wire/UserSearch/Types.hs +++ b/libs/wire-subsystems/src/Wire/UserSearch/Types.hs @@ -29,6 +29,7 @@ import Data.ByteString.Lazy import Data.Handle import Data.Id import Data.Json.Util +import Data.Qualified import Data.Text.Encoding import Database.Bloodhound.Types import Imports @@ -76,7 +77,8 @@ data UserDoc = UserDoc udScimExternalId :: Maybe Text, udSso :: Maybe Sso, udEmailUnvalidated :: Maybe EmailAddress, - udSearchable :: Maybe Bool + udSearchable :: Maybe Bool, + udType :: Maybe UserType } deriving (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform UserDoc) @@ -100,7 +102,8 @@ instance ToJSON UserDoc where "scim_external_id" .= udScimExternalId ud, "sso" .= udSso ud, "email_unvalidated" .= udEmailUnvalidated ud, - "searchable" .= udSearchable ud + "searchable" .= udSearchable ud, + "type" .= udType ud ] instance FromJSON UserDoc where @@ -123,10 +126,34 @@ instance FromJSON UserDoc where <*> o .:? "sso" <*> o .:? "email_unvalidated" <*> o .:? "searchable" + <*> o .:? "type" searchVisibilityInboundFieldName :: Key searchVisibilityInboundFieldName = "search_visibility_inbound" +-- Qualified UserId is not included in `UserDoc`, so it needs to be +-- provided here. Monad will most likely be Identity (I promise we'll +-- always make up some name if missing) or Maybe (if no name, then no +-- contact). +userDocToContact :: (Monad m) => Qualified UserId -> (Maybe Name -> m Text) -> UserDoc -> m Contact +userDocToContact contactQualifiedId getName userDoc = + getName userDoc.udName <&> \name -> + Contact + { contactQualifiedId, + contactName = name, + contactColorId = fromIntegral . fromColourId <$> userDoc.udColourId, + contactHandle = fromHandle <$> userDoc.udHandle, + contactTeam = userDoc.udTeam, + contactType = + -- NB: if you have index entries for bots that haven't + -- migrated yet, they will identify as regular users in + -- the search result. this is an accepted limitation. as + -- long as we have cassandra and elastic search involved, + -- the only way around it would be looking up serverIds in + -- cassandra for every query. + fromMaybe UserTypeRegular userDoc.udType + } + userDocToTeamContact :: [UserGroupId] -> UserDoc -> TeamContact userDocToTeamContact userGroups UserDoc {..} = TeamContact diff --git a/libs/wire-subsystems/src/Wire/UserStore/IndexUser.hs b/libs/wire-subsystems/src/Wire/UserStore/IndexUser.hs index b833bbfaad..7ac88be08f 100644 --- a/libs/wire-subsystems/src/Wire/UserStore/IndexUser.hs +++ b/libs/wire-subsystems/src/Wire/UserStore/IndexUser.hs @@ -149,12 +149,13 @@ indexUserToVersion role IndexUser {..} = const () <$$> writeTimeBumper ] -indexUserToDoc :: SearchVisibilityInbound -> Maybe Role -> IndexUser -> UserDoc -indexUserToDoc searchVisInbound mRole IndexUser {..} = +indexUserToDoc :: SearchVisibilityInbound -> UserType -> Maybe Role -> IndexUser -> UserDoc +indexUserToDoc searchVisInbound type_ mRole IndexUser {..} = if shouldIndex then UserDoc - { udSearchable = value <$> searchable, + { udType = Just type_, + udSearchable = value <$> searchable, udEmailUnvalidated = value <$> unverifiedEmail, udSso = sso . value =<< ssoId, udScimExternalId = join $ scimExternalId <$> (value <$> managedBy) <*> (value <$> ssoId), @@ -214,7 +215,8 @@ normalized = transliterate (trans "Any-Latin; Latin-ASCII; Lower") emptyUserDoc :: UserId -> UserDoc emptyUserDoc uid = UserDoc - { udSearchable = Nothing, + { udType = Nothing, + udSearchable = Nothing, udEmailUnvalidated = Nothing, udSso = Nothing, udScimExternalId = Nothing, diff --git a/libs/wire-subsystems/src/Wire/UserSubsystem/Interpreter.hs b/libs/wire-subsystems/src/Wire/UserSubsystem/Interpreter.hs index 86be662b3c..5f7ba9c70f 100644 --- a/libs/wire-subsystems/src/Wire/UserSubsystem/Interpreter.hs +++ b/libs/wire-subsystems/src/Wire/UserSubsystem/Interpreter.hs @@ -451,13 +451,11 @@ getLocalUserProfileImpl emailVisibilityConfigWithViewer luid = do lhs :: UserLegalHoldStatus <- do teamMember <- lift $ join <$> (internalGetTeamMember storedUser.id `mapM` storedUser.teamId) pure $ maybe defUserLegalHoldStatus (view legalHoldStatus) teamMember + userType <- lift $ getUserType storedUser.id storedUser.teamId storedUser.serviceId let user = mkUserFromStored domain locale storedUser - usrProfile = mkUserProfile emailVisibilityConfigWithViewer user lhs - app <- lift $ mapM (getApp storedUser.id) storedUser.teamId + usrProfile = mkUserProfile emailVisibilityConfigWithViewer userType user lhs lift $ deleteLocalIfExpired user - pure $ case join app of - Nothing -> usrProfile - Just _ -> usrProfile {profileType = UserTypeApp} + pure $ usrProfile getSelfProfileImpl :: ( Member (Input UserSubsystemConfig) r, @@ -578,6 +576,7 @@ guardLockedHandleField user updateOrigin handle = do updateUserProfileImpl :: ( Member UserStore r, + Member AppStore r, Member (Error UserSubsystemError) r, Member Events r, Member GalleyAPIAccess r, @@ -642,6 +641,7 @@ updateHandleImpl :: Member GalleyAPIAccess r, Member Events r, Member UserStore r, + Member AppStore r, Member IndexedUserStore r, Member Metrics r ) => @@ -708,13 +708,14 @@ checkHandlesImpl check num = reverse <$> collectFree [] check num syncUserIndex :: forall r. ( Member UserStore r, + Member AppStore r, Member GalleyAPIAccess r, Member IndexedUserStore r, Member Metrics r ) => UserId -> Sem r () -syncUserIndex uid = do +syncUserIndex uid = getIndexUser uid >>= maybe deleteFromIndex upsert where @@ -731,8 +732,9 @@ syncUserIndex uid = do (teamSearchVisibilityInbound . value) indexUser.teamId tm <- maybe (pure Nothing) (selectTeamMember . value) indexUser.teamId + userType <- getUserType indexUser.userId (indexUser.teamId <&> (.value)) (indexUser.serviceId <&> (.value)) let mRole = tm >>= mkRoleWithWriteTime - userDoc = indexUserToDoc vis (value <$> mRole) indexUser + userDoc = indexUserToDoc vis userType (value <$> mRole) indexUser version = ES.ExternalGT . ES.ExternalDocVersion . docVersion $ indexUserToVersion mRole indexUser Metrics.incCounter indexUpdateCounter IndexedUserStore.upsert (userIdToDocId uid) userDoc version @@ -760,6 +762,7 @@ searchUsersImpl :: forall r fedM. ( Member UserStore r, Member GalleyAPIAccess r, + Member AppStore r, Member (Error UserSubsystemError) r, Member IndexedUserStore r, Member FederationConfigStore r, @@ -795,6 +798,7 @@ searchUsersImpl searcherId searchTerm maybeDomain maybeMaxResults = do searchLocally :: forall r. ( Member GalleyAPIAccess r, + Member AppStore r, Member UserStore r, Member IndexedUserStore r, Member (Input UserSubsystemConfig) r @@ -823,7 +827,7 @@ searchLocally searcher searchTerm maybeMaxResults = do esMaxResults else pure $ SearchResult 0 0 0 [] FullSearch Nothing Nothing - let esContacts = map userDocToContact (searchResults esResult) + let esContacts = map userDocToContact' (searchResults esResult) -- Prepend results matching exact handle and results from ES. allContacts = case maybeExactHandleMatch of Nothing -> esContacts @@ -839,15 +843,13 @@ searchLocally searcher searchTerm maybeMaxResults = do handleTeamVisibility _ SearchVisibilityStandard = AllUsers handleTeamVisibility t SearchVisibilityNoNameOutsideTeam = TeamOnly t - userDocToContact :: UserDoc -> Contact - userDocToContact userDoc = - Contact - { contactQualifiedId = tUntagged $ qualifyAs searcher userDoc.udId, - contactName = maybe "" fromName userDoc.udName, - contactColorId = fromIntegral . fromColourId <$> userDoc.udColourId, - contactHandle = Handle.fromHandle <$> userDoc.udHandle, - contactTeam = userDoc.udTeam - } + userDocToContact' :: UserDoc -> Contact + userDocToContact' userDoc = + runIdentity $ + userDocToContact + (tUntagged $ qualifyAs searcher userDoc.udId) + (Identity . maybe "" fromName) + userDoc mkTeamSearchInfo :: Maybe TeamId -> Sem r TeamSearchInfo mkTeamSearchInfo searcherTeamId = do @@ -864,27 +866,26 @@ searchLocally searcher searchTerm maybeMaxResults = do exactHandleSearch :: Sem r (Maybe Contact) exactHandleSearch = runMaybeT $ do - handle <- MaybeT . pure $ Handle.parseHandle searchTerm + handle <- hoistMaybe $ Handle.parseHandle searchTerm owner <- MaybeT $ UserStore.lookupHandle handle storedUser <- MaybeT $ UserStore.getUser owner config <- lift input - let contact = contactFromStoredUser (tDomain searcher) storedUser - isContactVisible = + let isContactVisible = (config.searchSameTeamOnly && (snd . tUnqualified $ searcher) == storedUser.teamId) || (not config.searchSameTeamOnly) if isContactVisible && fromMaybe True storedUser.searchable - then pure contact - else MaybeT $ pure Nothing - - contactFromStoredUser :: Domain -> StoredUser -> Contact - contactFromStoredUser domain storedUser = - Contact - { contactQualifiedId = Qualified storedUser.id domain, - contactName = fromName storedUser.name, - contactHandle = Handle.fromHandle <$> storedUser.handle, - contactColorId = Just . fromIntegral . fromColourId $ storedUser.accentId, - contactTeam = storedUser.teamId - } + then do + userType <- lift $ getUserType storedUser.id storedUser.teamId storedUser.serviceId + pure $ + Contact + { contactQualifiedId = Qualified storedUser.id (tDomain searcher), + contactName = fromName storedUser.name, + contactHandle = Handle.fromHandle <$> storedUser.handle, + contactColorId = Just . fromIntegral . fromColourId $ storedUser.accentId, + contactTeam = storedUser.teamId, + contactType = userType + } + else hoistMaybe Nothing searchRemotely :: ( Member FederationConfigStore r, @@ -1053,6 +1054,7 @@ getAccountsByImpl (tSplit -> (domain, GetBy {includePendingInvitations, getByHan acceptTeamInvitationImpl :: ( Member (Input UserSubsystemConfig) r, Member UserStore r, + Member AppStore r, Member GalleyAPIAccess r, Member (Error UserSubsystemError) r, Member InvitationStore r, @@ -1117,6 +1119,7 @@ getUserExportDataImpl uid = fmap hush . runError @() $ do removeEmailEitherImpl :: ( Member UserKeyStore r, Member UserStore r, + Member AppStore r, Member Events r, Member IndexedUserStore r, Member (Input UserSubsystemConfig) r, @@ -1151,6 +1154,7 @@ checkUserIsAdminImpl uid = do setUserSearchableImpl :: ( Member UserStore r, + Member AppStore r, Member (Error UserSubsystemError) r, Member TeamSubsystem r, Member GalleyAPIAccess r, @@ -1166,3 +1170,20 @@ setUserSearchableImpl luid uid searchable = do ensurePermissions (tUnqualified luid) tid [SetMemberSearchable] UserStore.setUserSearchable uid searchable syncUserIndex uid + +-- * Helpers + +getUserType :: + forall r. + (Member AppStore r) => + UserId -> + Maybe TeamId -> + Maybe ServiceId -> + Sem r UserType +getUserType uid mTid mbServiceId = case mbServiceId of + Just _ -> pure UserTypeBot + Nothing -> do + mmApp <- mapM (getApp uid) mTid + case join mmApp of + Just _ -> pure UserTypeApp + Nothing -> pure UserTypeRegular diff --git a/libs/wire-subsystems/test/unit/Wire/AuthenticationSubsystem/InterpreterSpec.hs b/libs/wire-subsystems/test/unit/Wire/AuthenticationSubsystem/InterpreterSpec.hs index 1ff35582da..dfb4329076 100644 --- a/libs/wire-subsystems/test/unit/Wire/AuthenticationSubsystem/InterpreterSpec.hs +++ b/libs/wire-subsystems/test/unit/Wire/AuthenticationSubsystem/InterpreterSpec.hs @@ -42,6 +42,7 @@ import Wire.API.User import Wire.API.User qualified as User import Wire.API.User.Auth import Wire.API.User.Password +import Wire.AppStore import Wire.AuthenticationSubsystem import Wire.AuthenticationSubsystem.Config import Wire.AuthenticationSubsystem.Interpreter @@ -81,7 +82,8 @@ type AllEffects = EmailSubsystem, UserStore, State [StoredUser], - State (Map EmailAddress [SentMail]) + State (Map EmailAddress [SentMail]), + State [StoredApp] ] runAllEffects :: Domain -> [User] -> Maybe [Text] -> Sem AllEffects a -> Either AuthenticationSubsystemError a @@ -92,6 +94,7 @@ runAllEffects localDomain preexistingUsers mAllowedEmailDomains = local = toLocalUnsafe localDomain () } in run + . evalState mempty . evalState mempty . evalState mempty . inMemoryUserStoreInterpreter diff --git a/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs b/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs index 204fc5ed73..fbd01bdd5b 100644 --- a/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs +++ b/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs @@ -522,10 +522,17 @@ miniGetAllProfiles :: Sem r [UserProfile] miniGetAllProfiles = do users <- gets (.users) + apps <- gets (.apps) dom <- input pure $ map - (\u -> mkUserProfileWithEmail Nothing (mkUserFromStored dom miniLocale u) defUserLegalHoldStatus) + ( \u -> + let userType + | any ((== u.id) . (.id)) apps = UserTypeApp + | isJust u.serviceId = UserTypeBot + | otherwise = UserTypeRegular + in mkUserProfileWithEmail Nothing userType (mkUserFromStored dom miniLocale u) defUserLegalHoldStatus + ) users miniGetUsersByIds :: [UserId] -> MiniFederationMonad 'Brig [UserProfile] diff --git a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/IndexedUserStore.hs b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/IndexedUserStore.hs index 85b27a86b3..f481ed7367 100644 --- a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/IndexedUserStore.hs +++ b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/IndexedUserStore.hs @@ -30,10 +30,7 @@ import Polysemy.State import Wire.API.Team.Size import Wire.API.User.Search import Wire.IndexedUserStore -import Wire.MockInterpreters.UserStore (storedUserToIndexUser) -import Wire.StoredUser import Wire.UserSearch.Types -import Wire.UserStore.IndexUser newtype OrdDocId = OrdDocId Text deriving (Show, Eq, Ord) @@ -54,17 +51,6 @@ emptyIndex = docs = mempty } -storedUserToDoc :: StoredUser -> UserDoc -storedUserToDoc user = - let indexUser = storedUserToIndexUser user - in indexUserToDoc defaultSearchVisibilityInbound Nothing indexUser - -indexFromStoredUsers :: [StoredUser] -> UserIndex -indexFromStoredUsers storedUsers = do - run . execState emptyIndex . inMemoryIndexedUserStoreInterpreter $ do - for_ storedUsers $ \storedUser -> - upsert (userIdToDocId storedUser.id) (storedUserToDoc storedUser) ES.NoVersionControl - runInMemoryIndexedUserStoreIntepreter :: InterpreterFor IndexedUserStore r runInMemoryIndexedUserStoreIntepreter = evalState emptyIndex diff --git a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs index 391e8305d1..6e2086242c 100644 --- a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs +++ b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs @@ -63,8 +63,9 @@ inMemoryUserStoreInterpreter = interpret $ \case if u.id == uid then u {emailUnvalidated = Just email} :: StoredUser else u - GetIndexUser uid -> - gets $ fmap storedUserToIndexUser . find (\user -> user.id == uid) + GetIndexUser uid -> do + mUser <- gets @[StoredUser] $ find (\user -> user.id == uid) + pure $ storedUserToIndexUser <$> mUser GetIndexUsersPaginated _pageSize _pagingState -> error "GetIndexUsersPaginated not implemented in inMemoryUserStoreInterpreter" UpdateUserHandleEither uid hUpdate -> runError $ modifyLocalUsers (traverse doUpdate) @@ -72,7 +73,7 @@ inMemoryUserStoreInterpreter = interpret $ \case doUpdate :: StoredUser -> Sem (Error StoredUserUpdateError : r) StoredUser doUpdate u | u.id == uid = do - handles <- gets $ mapMaybe (.handle) + handles <- gets @[StoredUser] $ mapMaybe (.handle) when ( hUpdate.old /= Just hUpdate.new @@ -87,7 +88,7 @@ inMemoryUserStoreInterpreter = interpret $ \case us <- get us' <- f us put us' - DeleteUser user -> modify $ filter (\u -> u.id /= User.userId user) + DeleteUser user -> modify @[StoredUser] $ filter (\u -> u.id /= User.userId user) LookupHandle h -> lookupHandleImpl h GlimpseHandle h -> lookupHandleImpl h LookupStatus uid -> lookupStatusImpl uid @@ -105,7 +106,7 @@ inMemoryUserStoreInterpreter = interpret $ \case doUpdate :: StoredUser -> StoredUser doUpdate u = if u.id == uid then u {email = Nothing} else u GetUserTeam uid -> do - gets $ \users -> do + gets @[StoredUser] $ \users -> do user <- find (\user -> user.id == uid) users user.teamId SetUserSearchable uid (SetSearchable searchable) -> modify $ map f diff --git a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserSubsystem.hs b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserSubsystem.hs index 71dc964440..fe979c1cf1 100644 --- a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserSubsystem.hs +++ b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserSubsystem.hs @@ -69,4 +69,4 @@ userSubsystemTestInterpreter initialUsers = SetUserSearchable {} -> error "SetUserSearchable: implement on demand (userSubsystemInterpreter)" toProfile :: User -> UserProfile -toProfile u = mkUserProfileWithEmail (userEmail u) u UserLegalHoldDisabled +toProfile u = mkUserProfileWithEmail (userEmail u) UserTypeRegular u UserLegalHoldDisabled diff --git a/libs/wire-subsystems/test/unit/Wire/UserSearch/TypesSpec.hs b/libs/wire-subsystems/test/unit/Wire/UserSearch/TypesSpec.hs index 4fae73f196..a09d56bd8f 100644 --- a/libs/wire-subsystems/test/unit/Wire/UserSearch/TypesSpec.hs +++ b/libs/wire-subsystems/test/unit/Wire/UserSearch/TypesSpec.hs @@ -64,7 +64,8 @@ userDoc1 = udScimExternalId = Nothing, udSso = Nothing, udEmailUnvalidated = Nothing, - udSearchable = Nothing + udSearchable = Nothing, + udType = Nothing } -- Dont touch this. This represents serialized legacy data. diff --git a/libs/wire-subsystems/test/unit/Wire/UserSubsystem/InterpreterSpec.hs b/libs/wire-subsystems/test/unit/Wire/UserSubsystem/InterpreterSpec.hs index edfb8af1f8..2965f37203 100644 --- a/libs/wire-subsystems/test/unit/Wire/UserSubsystem/InterpreterSpec.hs +++ b/libs/wire-subsystems/test/unit/Wire/UserSubsystem/InterpreterSpec.hs @@ -38,6 +38,7 @@ import Data.Set (insert, member, notMember) import Data.Set qualified as S import Data.String.Conversions (cs) import Data.Text.Encoding (encodeUtf8) +import Database.Bloodhound.Internal.Client qualified as ES import Imports import Polysemy import Polysemy.Error @@ -60,6 +61,7 @@ import Wire.API.User.Search import Wire.API.UserEvent import Wire.AuthenticationSubsystem.Error import Wire.DomainRegistrationStore qualified as DRS +import Wire.IndexedUserStore qualified as IU import Wire.InvitationStore (InsertInvitation, StoredInvitation) import Wire.InvitationStore qualified as InvitationStore import Wire.MiniBackend @@ -67,6 +69,8 @@ import Wire.MockInterpreters import Wire.RateLimit import Wire.StoredUser import Wire.UserKeyStore +import Wire.UserSearch.Types +import Wire.UserStore.IndexUser import Wire.UserSubsystem import Wire.UserSubsystem.Error import Wire.UserSubsystem.HandleBlacklist @@ -100,6 +104,7 @@ spec = describe "UserSubsystem.Interpreter" do mkExpectedProfiles domain users = [ mkUserProfileWithEmail Nothing + (if isJust targetUser.serviceId then UserTypeBot else UserTypeRegular) (mkUserFromStored domain miniLocale targetUser) defUserLegalHoldStatus | targetUser <- users @@ -159,6 +164,7 @@ spec = describe "UserSubsystem.Interpreter" do in retrievedProfiles === [ mkUserProfile (fmap (const $ (,) <$> viewer.teamId <*> Just teamMember) config.emailVisibilityConfig) + (if isJust targetUser.serviceId then UserTypeBot else UserTypeRegular) (mkUserFromStored domain config.defaultLocale targetUser) defUserLegalHoldStatus ] @@ -175,6 +181,7 @@ spec = describe "UserSubsystem.Interpreter" do in retrievedProfile === [ mkUserProfile (fmap (const Nothing) config.emailVisibilityConfig) + (if isJust targetUser.serviceId then UserTypeBot else UserTypeRegular) (mkUserFromStored domain config.defaultLocale targetUser) defUserLegalHoldStatus ] @@ -1091,6 +1098,19 @@ spec = describe "UserSubsystem.Interpreter" do \(ActiveStoredUser searcheeNoHandle) (searcheeHandle :: Handle) (ActiveStoredUser searcher) localDomain configBase -> let teamMember = mkTeamMember searcher.id fullPermissions Nothing defUserLegalHoldStatus searchee = searcheeNoHandle {handle = Just searcheeHandle} :: StoredUser + + storedUserToDoc :: StoredUser -> UserDoc + storedUserToDoc user = + let indexUser = storedUserToIndexUser user + userType = if isJust user.serviceId then UserTypeBot else UserTypeRegular + in indexUserToDoc defaultSearchVisibilityInbound userType Nothing indexUser + + indexFromStoredUsers :: [StoredUser] -> UserIndex + indexFromStoredUsers storedUsers = do + run . execState emptyIndex . inMemoryIndexedUserStoreInterpreter $ do + for_ storedUsers $ \storedUser -> + IU.upsert (userIdToDocId storedUser.id) (storedUserToDoc storedUser) ES.NoVersionControl + localBackend = def { users = [searchee, searcher], @@ -1111,6 +1131,7 @@ spec = describe "UserSubsystem.Interpreter" do contactQualifiedId = Qualified searchee.id localDomain, contactName = fromName searchee.name, contactHandle = fromHandle <$> searchee.handle, - contactColorId = Just . fromIntegral $ searchee.accentId.fromColourId + contactColorId = Just . fromIntegral $ searchee.accentId.fromColourId, + contactType = UserTypeRegular } pure $ result.searchResults === [expectedContact | fromMaybe True searchee.searchable] diff --git a/postgres-schema.sql b/postgres-schema.sql index 378195989b..8cfc62bd5d 100644 --- a/postgres-schema.sql +++ b/postgres-schema.sql @@ -9,8 +9,8 @@ \restrict 79bbfb4630959c48307653a5cd3d83f2582b3c2210f75f10d79e3ebf0015620 --- Dumped from database version 17.6 --- Dumped by pg_dump version 17.6 +-- Dumped from database version 17.7 +-- Dumped by pg_dump version 17.7 SET statement_timeout = 0; SET lock_timeout = 0; diff --git a/services/brig/src/Brig/Index/Eval.hs b/services/brig/src/Brig/Index/Eval.hs index dbc6a74f95..6f8dc1b1e4 100644 --- a/services/brig/src/Brig/Index/Eval.hs +++ b/services/brig/src/Brig/Index/Eval.hs @@ -39,16 +39,21 @@ import Data.Credentials (Credentials (..)) import Data.Id import Database.Bloodhound qualified as ES import Database.Bloodhound.Internal.Client (BHEnv (..)) +import Hasql.Pool +import Hasql.Pool.Extended import Imports import Polysemy import Polysemy.Embed (runEmbedded) import Polysemy.Error +import Polysemy.Input import Polysemy.TinyLog hiding (Logger) import System.Logger qualified as Log import System.Logger.Class (Logger) -import Util.Options (initCredentials) +import Util.Options import Wire.API.Federation.Client (FederatorClient) import Wire.API.Federation.Error +import Wire.AppStore +import Wire.AppStore.Postgres import Wire.BlockListStore (BlockListStore) import Wire.BlockListStore.Cassandra import Wire.FederationAPIAccess @@ -86,6 +91,7 @@ type BrigIndexEffectStack = FederationAPIAccess FederatorClient, Error FederationError, UserStore, + AppStore, IndexedUserStore, Error IndexedUserStoreError, IndexedUserMigrationStore, @@ -97,15 +103,18 @@ type BrigIndexEffectStack = Metrics, TinyLog, Concurrency 'Unsafe, + Input Pool, + Error UsageError, Embed IO, Final IO ] -runSem :: ESConnectionSettings -> CassandraSettings -> Endpoint -> Logger -> Sem BrigIndexEffectStack a -> IO a -runSem esConn cas galleyEndpoint logger action = do +runSem :: ESConnectionSettings -> CassandraSettings -> PostgresSettings -> Endpoint -> Logger -> Sem BrigIndexEffectStack a -> IO a +runSem esConn cas pg galleyEndpoint logger action = do mgr <- initHttpManagerWithTLSConfig esConn.esInsecureSkipVerifyTls esConn.esCaCert mEsCreds :: Maybe Credentials <- for esConn.esCredentials initCredentials casClient <- defInitCassandra (toCassandraOpts cas) logger + pgPool <- initPostgresPool pg.pool pg.settings pg.password let bhEnv = BHEnv { bhServer = toESServer esConn.esServer, @@ -125,6 +134,8 @@ runSem esConn cas galleyEndpoint logger action = do migrationIndexName = fromMaybe defaultMigrationIndexName (esMigrationIndexName esConn) runFinal . embedToFinal + . throwErrorToIOFinal @UsageError + . runInputConst pgPool . unsafelyPerformConcurrency . loggerToTinyLogReqId reqId logger . ignoreMetrics @@ -138,6 +149,7 @@ runSem esConn cas galleyEndpoint logger action = do . interpretIndexedUserMigrationStoreES bhEnv migrationIndexName . throwErrorToIOFinal @IndexedUserStoreError . interpretIndexedUserStoreES indexedUserStoreConfig + . interpretAppStoreToPostgres . interpretUserStoreCassandra casClient . throwErrorToIOFinal @FederationError . noFederationAPIAccess @@ -161,17 +173,17 @@ runCommand l = \case Reset es galley -> do e <- initIndex l (es ^. esConnection) galley runIndexIO e $ resetIndex (mkCreateIndexSettings es) - Reindex es cas galley -> do - runSem (es ^. esConnection) cas galley l $ + Reindex es cas pg galley -> do + runSem (es ^. esConnection) cas pg galley l $ IndexedUserStoreBulk.syncAllUsers - ReindexSameOrNewer es cas galley -> do - runSem (es ^. esConnection) cas galley l $ + ReindexSameOrNewer es cas pg galley -> do + runSem (es ^. esConnection) cas pg galley l $ IndexedUserStoreBulk.forceSyncAllUsers UpdateMapping esConn galley -> do e <- initIndex l esConn galley runIndexIO e updateMapping - Migrate es cas galley -> do - runSem (es ^. esConnection) cas galley l $ + Migrate es cas pg galley -> do + runSem (es ^. esConnection) cas pg galley l $ IndexedUserStoreBulk.migrateData ReindexFromAnotherIndex reindexSettings -> do mgr <- diff --git a/services/brig/src/Brig/Index/Options.hs b/services/brig/src/Brig/Index/Options.hs index f72db00304..67b5907022 100644 --- a/services/brig/src/Brig/Index/Options.hs +++ b/services/brig/src/Brig/Index/Options.hs @@ -29,6 +29,7 @@ module Brig.Index.Options esIndexRefreshInterval, esDeleteTemplate, CassandraSettings, + PostgresSettings (..), toCassandraOpts, cHost, cPort, @@ -54,6 +55,7 @@ import Data.Text qualified as Text import Data.Text.Strict.Lens import Data.Time.Clock (NominalDiffTime) import Database.Bloodhound qualified as ES +import Hasql.Pool.Extended import Imports import Options.Applicative import URI.ByteString @@ -63,11 +65,11 @@ import Util.Options (CassandraOpts (..), Endpoint (..), FilePathSecrets) data Command = Create ElasticSettings Endpoint | Reset ElasticSettings Endpoint - | Reindex ElasticSettings CassandraSettings Endpoint - | ReindexSameOrNewer ElasticSettings CassandraSettings Endpoint + | Reindex ElasticSettings CassandraSettings PostgresSettings Endpoint + | ReindexSameOrNewer ElasticSettings CassandraSettings PostgresSettings Endpoint | -- | 'ElasticSettings' has shards and other settings that are not needed here. UpdateMapping ESConnectionSettings Endpoint - | Migrate ElasticSettings CassandraSettings Endpoint + | Migrate ElasticSettings CassandraSettings PostgresSettings Endpoint | ReindexFromAnotherIndex ReindexFromAnotherIndexSettings deriving (Show) @@ -98,6 +100,13 @@ data CassandraSettings = CassandraSettings } deriving (Show) +data PostgresSettings = PostgresSettings + { pool :: PoolConfig, + settings :: Map Text Text, + password :: Maybe FilePathSecrets + } + deriving (Show) + data ReindexFromAnotherIndexSettings = ReindexFromAnotherIndexSettings { _reindexEsConnection :: ESConnectionSettings, _reindexDestIndex :: ES.IndexName, @@ -331,6 +340,9 @@ cassandraSettingsParser = ) ) +postgresSettingsParser :: Parser PostgresSettings +postgresSettingsParser = todo + reindexToAnotherIndexSettingsParser :: Parser ReindexFromAnotherIndexSettings reindexToAnotherIndexSettingsParser = ReindexFromAnotherIndexSettings @@ -394,19 +406,19 @@ commandParser = <> command "reindex" ( info - (Reindex <$> elasticSettingsParser <*> cassandraSettingsParser <*> galleyEndpointParser) + (Reindex <$> elasticSettingsParser <*> cassandraSettingsParser <*> postgresSettingsParser <*> galleyEndpointParser) (progDesc "Reindex all users from Cassandra if there is a new version.") ) <> command "reindex-if-same-or-newer" ( info - (ReindexSameOrNewer <$> elasticSettingsParser <*> cassandraSettingsParser <*> galleyEndpointParser) + (ReindexSameOrNewer <$> elasticSettingsParser <*> cassandraSettingsParser <*> postgresSettingsParser <*> galleyEndpointParser) (progDesc "Reindex all users from Cassandra, even if the version has not changed.") ) <> command "migrate-data" ( info - (Migrate <$> elasticSettingsParser <*> cassandraSettingsParser <*> galleyEndpointParser) + (Migrate <$> elasticSettingsParser <*> cassandraSettingsParser <*> postgresSettingsParser <*> galleyEndpointParser) (progDesc "Migrate data in elastic search") ) <> command diff --git a/services/brig/src/Brig/Provider/API.hs b/services/brig/src/Brig/Provider/API.hs index 603aeaa5e4..86c7ffb13e 100644 --- a/services/brig/src/Brig/Provider/API.hs +++ b/services/brig/src/Brig/Provider/API.hs @@ -898,7 +898,7 @@ guardConvAdmin conv = do botGetSelf :: BotId -> (Handler r) Public.UserProfile botGetSelf bot = do p <- lift $ wrapClient $ User.lookupUser NoPendingInvitations (botUserId bot) - maybe (throwStd (errorToWai @'E.UserNotFound)) (\u -> pure $ Public.mkUserProfile EmailVisibleToSelf u UserLegalHoldNoConsent) p + maybe (throwStd (errorToWai @'E.UserNotFound)) (\u -> pure $ Public.mkUserProfile EmailVisibleToSelf UserTypeBot u UserLegalHoldNoConsent) p botGetClient :: (Member GalleyAPIAccess r) => BotId -> (Handler r) (Maybe Public.Client) botGetClient bot = do diff --git a/services/brig/src/Brig/User/API/Handle.hs b/services/brig/src/Brig/User/API/Handle.hs index 0b97637255..ecad744a94 100644 --- a/services/brig/src/Brig/User/API/Handle.hs +++ b/services/brig/src/Brig/User/API/Handle.hs @@ -101,5 +101,6 @@ contactFromProfile profile = contactName = fromName $ profileName profile, contactHandle = fromHandle <$> profileHandle profile, contactColorId = Just . fromIntegral . fromColourId $ profileAccentId profile, - contactTeam = profileTeam profile + contactTeam = profileTeam profile, + contactType = profileType profile } diff --git a/services/brig/src/Brig/User/Search/Index.hs b/services/brig/src/Brig/User/Search/Index.hs index 634f059559..4c4919729d 100644 --- a/services/brig/src/Brig/User/Search/Index.hs +++ b/services/brig/src/Brig/User/Search/Index.hs @@ -466,6 +466,14 @@ indexMapping = mpIndex = True, mpAnalyzer = Nothing, mpFields = mempty + }, + "type" + .= MappingProperty + { mpType = MPKeyword, + mpStore = False, + mpIndex = True, + mpAnalyzer = Nothing, + mpFields = mempty } ] ] diff --git a/services/brig/src/Brig/User/Search/SearchIndex.hs b/services/brig/src/Brig/User/Search/SearchIndex.hs index da1458aa41..41839c7a25 100644 --- a/services/brig/src/Brig/User/Search/SearchIndex.hs +++ b/services/brig/src/Brig/User/Search/SearchIndex.hs @@ -35,7 +35,7 @@ import Data.Id import Data.Qualified (Qualified (Qualified)) import Database.Bloodhound qualified as ES import Imports hiding (log, searchable) -import Wire.API.User (ColourId (..), Name (fromName)) +import Wire.API.User (ColourId (..), Name (fromName), UserType (UserTypeRegular)) import Wire.API.User.Search import Wire.IndexedUserStore (IndexedUserStoreError (..)) import Wire.IndexedUserStore.ElasticSearch (mappingName) @@ -80,7 +80,7 @@ queryIndex (IndexQuery q f _) s = do r <- ES.searchByType idx mappingName search >>= ES.parseEsResponse @_ @(ES.SearchResult UserDoc) - either (throwM . IndexLookupError) (traverse (userDocToContact localDomain) . mkResult) r + either (throwM . IndexLookupError) (traverse (userDocToContact' localDomain) . mkResult) r where mkResult es = let results = mapMaybe ES.hitSource . ES.hits . ES.searchHits $ es @@ -94,14 +94,12 @@ queryIndex (IndexQuery q f _) s = do searchHasMore = Nothing } -userDocToContact :: (MonadThrow m) => Domain -> UserDoc -> m Contact -userDocToContact localDomain UserDoc {..} = do - let contactQualifiedId = Qualified udId localDomain - contactName <- maybe (throwM $ IndexError "Name not found") (pure . fromName) udName - let contactColorId = fromIntegral . fromColourId <$> udColourId - contactHandle = fromHandle <$> udHandle - contactTeam = udTeam - pure $ Contact {..} + userDocToContact' :: (MonadThrow m) => Domain -> UserDoc -> m Contact -- !! + userDocToContact' localDomain userDoc = + userDocToContact + (Qualified userDoc.udId localDomain) + (maybe (throwM $ IndexError "Name not found") (pure . fromName) userDoc.udName) + userDoc -- | The default or canonical 'IndexQuery'. -- diff --git a/services/brig/test/integration/API/Search.hs b/services/brig/test/integration/API/Search.hs index a4809993f0..1e4a915bb4 100644 --- a/services/brig/test/integration/API/Search.hs +++ b/services/brig/test/integration/API/Search.hs @@ -40,7 +40,7 @@ import Brig.App (initHttpManagerWithTLSConfig) import Brig.Index.Eval (initIndex, runCommand) import Brig.Index.Options import Brig.Index.Options qualified as IndexOpts -import Brig.Options (ElasticSearchOpts) +import Brig.Options import Brig.Options qualified as Opt import Brig.Options qualified as Opts import Brig.User.Search.Index @@ -800,7 +800,7 @@ runReindexFromAnotherIndex logger opts newIndexName migrationIndexName = in runCommand logger $ ReindexFromAnotherIndex reindexSettings runReindexFromDatabase :: - (ElasticSettings -> CassandraSettings -> Endpoint -> Command) -> + (ElasticSettings -> CassandraSettings -> PostgresSettings -> Endpoint -> Command) -> Log.Logger -> Opt.Opts -> ES.IndexName -> @@ -824,9 +824,11 @@ runReindexFromDatabase syncCommand logger opts newIndexName migrationIndexName = & IndexOpts.cPort .~ (opts.cassandra.endpoint.port) & IndexOpts.cKeyspace .~ (C.Keyspace opts.cassandra.keyspace) ) + postgresSettings :: PostgresSettings = + PostgresSettings opts.postgresqlPool opts.postgresql opts.postgresqlPassword endpoint :: Endpoint = opts.galley - in runCommand logger $ syncCommand elasticSettings cassandraSettings endpoint + in runCommand logger $ syncCommand elasticSettings cassandraSettings postgresSettings endpoint toESConnectionSettings :: ElasticSearchOpts -> ES.IndexName -> ESConnectionSettings toESConnectionSettings opts migrationIndexName = ESConnectionSettings {..} diff --git a/services/federator/test/integration/Test/Federator/IngressSpec.hs b/services/federator/test/integration/Test/Federator/IngressSpec.hs index 41b9f42af3..be2b1c1627 100644 --- a/services/federator/test/integration/Test/Federator/IngressSpec.hs +++ b/services/federator/test/integration/Test/Federator/IngressSpec.hs @@ -59,7 +59,7 @@ spec env = do brig <- view teBrig <$> ask user <- randomUser brig - let expectedProfile = mkUserProfile EmailVisibleToSelf user UserLegalHoldNoConsent + let expectedProfile = mkUserProfile EmailVisibleToSelf UserTypeRegular user UserLegalHoldNoConsent runTestSem $ do resp <- liftToCodensity diff --git a/services/federator/test/integration/Test/Federator/InwardSpec.hs b/services/federator/test/integration/Test/Federator/InwardSpec.hs index 1daee8e77e..180d41b981 100644 --- a/services/federator/test/integration/Test/Federator/InwardSpec.hs +++ b/services/federator/test/integration/Test/Federator/InwardSpec.hs @@ -70,7 +70,7 @@ spec env = brig <- view teBrig <$> ask user <- randomUser brig - let expectedProfile = mkUserProfile EmailVisibleToSelf user UserLegalHoldNoConsent + let expectedProfile = mkUserProfile EmailVisibleToSelf UserTypeRegular user UserLegalHoldNoConsent bdy <- responseJsonError =<< inwardCall "/federation/brig/get-users-by-ids" (encode [userId user])