diff --git a/changelog.d/5-internal/polysemy-store b/changelog.d/5-internal/polysemy-store
index 98d604a47a..92950ac7ef 100644
--- a/changelog.d/5-internal/polysemy-store
+++ b/changelog.d/5-internal/polysemy-store
@@ -1 +1 @@
-Add polysemy store effects and split off Cassandra specific functionality from the Galley.Data module hierarchy.
+Add polysemy store effects and split off Cassandra specific functionality from the Galley.Data module hierarchy (#1890, #1906).
diff --git a/services/galley/galley.cabal b/services/galley/galley.cabal
index fe434196d1..2a18dcb467 100644
--- a/services/galley/galley.cabal
+++ b/services/galley/galley.cabal
@@ -4,7 +4,7 @@ cabal-version: 1.12
--
-- see: https://github.com/sol/hpack
--
--- hash: f4fae64cc086ec4a37984b47611854d490e7d34ba17147a2e53d2b11c3ca3218
+-- hash: 07288a51b3ae662362fae1279cdd4c22977ae3c8dfb4dffe1228d0da1beba2b4
name: galley
version: 0.83.0
@@ -46,26 +46,27 @@ library
Galley.App
Galley.Aws
Galley.Cassandra
+ Galley.Cassandra.Access
Galley.Cassandra.Client
Galley.Cassandra.Code
Galley.Cassandra.Conversation
Galley.Cassandra.Conversation.Members
Galley.Cassandra.ConversationList
+ Galley.Cassandra.CustomBackend
Galley.Cassandra.LegalHold
Galley.Cassandra.Paging
+ Galley.Cassandra.Queries
+ Galley.Cassandra.ResultSet
+ Galley.Cassandra.SearchVisibility
Galley.Cassandra.Services
Galley.Cassandra.Store
Galley.Cassandra.Team
- Galley.Data.Access
+ Galley.Cassandra.TeamFeatures
+ Galley.Cassandra.TeamNotifications
Galley.Data.Conversation
Galley.Data.Conversation.Types
- Galley.Data.CustomBackend
- Galley.Data.Instances
- Galley.Data.LegalHold
- Galley.Data.Queries
- Galley.Data.ResultSet
+ Galley.Cassandra.Instances
Galley.Data.Scope
- Galley.Data.SearchVisibility
Galley.Data.Services
Galley.Data.TeamFeatures
Galley.Data.TeamNotifications
@@ -76,17 +77,22 @@ library
Galley.Effects.ClientStore
Galley.Effects.CodeStore
Galley.Effects.ConversationStore
+ Galley.Effects.CustomBackendStore
Galley.Effects.ExternalAccess
Galley.Effects.FederatorAccess
Galley.Effects.FireAndForget
Galley.Effects.GundeckAccess
+ Galley.Effects.LegalHoldStore
Galley.Effects.ListItems
Galley.Effects.MemberStore
Galley.Effects.Paging
Galley.Effects.RemoteConversationListStore
+ Galley.Effects.SearchVisibilityStore
Galley.Effects.ServiceStore
Galley.Effects.SparAccess
+ Galley.Effects.TeamFeatureStore
Galley.Effects.TeamMemberStore
+ Galley.Effects.TeamNotificationStore
Galley.Effects.TeamStore
Galley.Env
Galley.External
diff --git a/services/galley/src/Galley/API/Create.hs b/services/galley/src/Galley/API/Create.hs
index 39f75a9733..ee86e14933 100644
--- a/services/galley/src/Galley/API/Create.hs
+++ b/services/galley/src/Galley/API/Create.hs
@@ -39,7 +39,6 @@ import Galley.API.Mapping
import Galley.API.One2One
import Galley.API.Util
import Galley.App
-import Galley.Data.Access
import qualified Galley.Data.Conversation as Data
import Galley.Data.Conversation.Types
import Galley.Effects
@@ -73,7 +72,15 @@ import Wire.API.Team.LegalHold (LegalholdProtectee (LegalholdPlusFederationNotIm
--
-- See Note [managed conversations].
createGroupConversation ::
- Members '[ConversationStore, BrigAccess, FederatorAccess, GundeckAccess, TeamStore] r =>
+ Members
+ '[ ConversationStore,
+ BrigAccess,
+ FederatorAccess,
+ GundeckAccess,
+ LegalHoldStore,
+ TeamStore
+ ]
+ r =>
UserId ->
ConnId ->
Public.NewConvUnmanaged ->
@@ -86,7 +93,15 @@ createGroupConversation user conn wrapped@(Public.NewConvUnmanaged body) =
-- | An internal endpoint for creating managed group conversations. Will
-- throw an error for everything else.
internalCreateManagedConversationH ::
- Members '[ConversationStore, BrigAccess, FederatorAccess, GundeckAccess, TeamStore] r =>
+ Members
+ '[ ConversationStore,
+ BrigAccess,
+ FederatorAccess,
+ GundeckAccess,
+ LegalHoldStore,
+ TeamStore
+ ]
+ r =>
UserId ::: ConnId ::: JsonRequest NewConvManaged ->
Galley r Response
internalCreateManagedConversationH (zusr ::: zcon ::: req) = do
@@ -94,7 +109,15 @@ internalCreateManagedConversationH (zusr ::: zcon ::: req) = do
handleConversationResponse <$> internalCreateManagedConversation zusr zcon newConv
internalCreateManagedConversation ::
- Members '[ConversationStore, BrigAccess, FederatorAccess, GundeckAccess, TeamStore] r =>
+ Members
+ '[ ConversationStore,
+ BrigAccess,
+ FederatorAccess,
+ GundeckAccess,
+ LegalHoldStore,
+ TeamStore
+ ]
+ r =>
UserId ->
ConnId ->
NewConvManaged ->
@@ -105,7 +128,7 @@ internalCreateManagedConversation zusr zcon (NewConvManaged body) = do
Just tinfo -> createTeamGroupConv zusr zcon tinfo body
ensureNoLegalholdConflicts ::
- Member TeamStore r =>
+ Members '[LegalHoldStore, TeamStore] r =>
[Remote UserId] ->
[UserId] ->
Galley r ()
@@ -117,7 +140,15 @@ ensureNoLegalholdConflicts remotes locals = do
-- | A helper for creating a regular (non-team) group conversation.
createRegularGroupConv ::
- Members '[ConversationStore, BrigAccess, FederatorAccess, GundeckAccess, TeamStore] r =>
+ Members
+ '[ ConversationStore,
+ BrigAccess,
+ FederatorAccess,
+ GundeckAccess,
+ LegalHoldStore,
+ TeamStore
+ ]
+ r =>
UserId ->
ConnId ->
NewConvUnmanaged ->
@@ -155,6 +186,7 @@ createTeamGroupConv ::
BrigAccess,
FederatorAccess,
GundeckAccess,
+ LegalHoldStore,
TeamStore
]
r =>
@@ -497,11 +529,11 @@ toUUIDs a b = do
return (a', b')
accessRole :: NewConv -> AccessRole
-accessRole b = fromMaybe defRole (newConvAccessRole b)
+accessRole b = fromMaybe Data.defRole (newConvAccessRole b)
access :: NewConv -> [Access]
access a = case Set.toList (newConvAccess a) of
- [] -> defRegularConvAccess
+ [] -> Data.defRegularConvAccess
(x : xs) -> x : xs
newConvMembers :: Local x -> NewConv -> UserList UserId
diff --git a/services/galley/src/Galley/API/CustomBackend.hs b/services/galley/src/Galley/API/CustomBackend.hs
index fa98803c79..9630ca6b4c 100644
--- a/services/galley/src/Galley/API/CustomBackend.hs
+++ b/services/galley/src/Galley/API/CustomBackend.hs
@@ -27,37 +27,41 @@ import Data.Domain (Domain)
import Galley.API.Error
import Galley.API.Util
import Galley.App
-import qualified Galley.Data.CustomBackend as Data
+import Galley.Effects.CustomBackendStore
import Galley.Types
import Imports hiding ((\\))
import Network.HTTP.Types
import Network.Wai
import Network.Wai.Predicate hiding (setStatus)
import Network.Wai.Utilities
+import Polysemy
import qualified Wire.API.CustomBackend as Public
-- PUBLIC ---------------------------------------------------------------------
-getCustomBackendByDomainH :: Domain ::: JSON -> Galley r Response
+getCustomBackendByDomainH :: Member CustomBackendStore r => Domain ::: JSON -> Galley r Response
getCustomBackendByDomainH (domain ::: _) =
json <$> getCustomBackendByDomain domain
-getCustomBackendByDomain :: Domain -> Galley r Public.CustomBackend
+getCustomBackendByDomain :: Member CustomBackendStore r => Domain -> Galley r Public.CustomBackend
getCustomBackendByDomain domain =
- Data.getCustomBackend domain >>= \case
+ liftSem (getCustomBackend domain) >>= \case
Nothing -> throwM (customBackendNotFound domain)
Just customBackend -> pure customBackend
-- INTERNAL -------------------------------------------------------------------
-internalPutCustomBackendByDomainH :: Domain ::: JsonRequest CustomBackend -> Galley r Response
+internalPutCustomBackendByDomainH ::
+ Member CustomBackendStore r =>
+ Domain ::: JsonRequest CustomBackend ->
+ Galley r Response
internalPutCustomBackendByDomainH (domain ::: req) = do
customBackend <- fromJsonBody req
-- simple enough to not need a separate function
- Data.setCustomBackend domain customBackend
+ liftSem $ setCustomBackend domain customBackend
pure (empty & setStatus status201)
-internalDeleteCustomBackendByDomainH :: Domain ::: JSON -> Galley r Response
+internalDeleteCustomBackendByDomainH :: Member CustomBackendStore r => Domain ::: JSON -> Galley r Response
internalDeleteCustomBackendByDomainH (domain ::: _) = do
- Data.deleteCustomBackend domain
+ liftSem $ deleteCustomBackend domain
pure (empty & setStatus status200)
diff --git a/services/galley/src/Galley/API/Federation.hs b/services/galley/src/Galley/API/Federation.hs
index fcadd8f171..a6ba85d575 100644
--- a/services/galley/src/Galley/API/Federation.hs
+++ b/services/galley/src/Galley/API/Federation.hs
@@ -246,6 +246,7 @@ leaveConversation ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
MemberStore,
TeamStore
]
diff --git a/services/galley/src/Galley/API/LegalHold.hs b/services/galley/src/Galley/API/LegalHold.hs
index 2f6d2a63c7..0a6015259e 100644
--- a/services/galley/src/Galley/API/LegalHold.hs
+++ b/services/galley/src/Galley/API/LegalHold.hs
@@ -54,12 +54,11 @@ import Galley.API.Util
import Galley.App
import Galley.Cassandra.Paging
import qualified Galley.Data.Conversation as Data
-import Galley.Data.LegalHold (isTeamLegalholdWhitelisted)
-import qualified Galley.Data.LegalHold as LegalHoldData
-import qualified Galley.Data.TeamFeatures as TeamFeatures
import Galley.Effects
import Galley.Effects.BrigAccess
+import qualified Galley.Effects.LegalHoldStore as LegalHoldData
import Galley.Effects.Paging
+import qualified Galley.Effects.TeamFeatureStore as TeamFeatures
import Galley.Effects.TeamMemberStore
import Galley.Effects.TeamStore
import qualified Galley.External.LegalHoldService as LHService
@@ -80,25 +79,27 @@ import qualified Wire.API.Team.Feature as Public
import Wire.API.Team.LegalHold (LegalholdProtectee (LegalholdPlusFederationNotImplemented))
import qualified Wire.API.Team.LegalHold as Public
-assertLegalHoldEnabledForTeam :: TeamId -> Galley r ()
+assertLegalHoldEnabledForTeam :: Members '[LegalHoldStore, TeamFeatureStore] r => TeamId -> Galley r ()
assertLegalHoldEnabledForTeam tid = unlessM (isLegalHoldEnabledForTeam tid) $ throwM legalHoldNotEnabled
-isLegalHoldEnabledForTeam :: TeamId -> Galley r Bool
+isLegalHoldEnabledForTeam :: Members '[LegalHoldStore, TeamFeatureStore] r => TeamId -> Galley r Bool
isLegalHoldEnabledForTeam tid = do
view (options . Opts.optSettings . Opts.setFeatureFlags . flagLegalHold) >>= \case
FeatureLegalHoldDisabledPermanently -> do
pure False
FeatureLegalHoldDisabledByDefault -> do
- statusValue <- Public.tfwoStatus <$$> TeamFeatures.getFeatureStatusNoConfig @'Public.TeamFeatureLegalHold tid
+ statusValue <-
+ liftSem $
+ Public.tfwoStatus <$$> TeamFeatures.getFeatureStatusNoConfig @'Public.TeamFeatureLegalHold tid
return $ case statusValue of
Just Public.TeamFeatureEnabled -> True
Just Public.TeamFeatureDisabled -> False
Nothing -> False
FeatureLegalHoldWhitelistTeamsAndImplicitConsent ->
- isTeamLegalholdWhitelisted tid
+ liftSem $ LegalHoldData.isTeamLegalholdWhitelisted tid
createSettingsH ::
- Member TeamStore r =>
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
UserId ::: TeamId ::: JsonRequest Public.NewLegalHoldService ::: JSON ->
Galley r Response
createSettingsH (zusr ::: tid ::: req ::: _) = do
@@ -106,7 +107,7 @@ createSettingsH (zusr ::: tid ::: req ::: _) = do
setStatus status201 . json <$> createSettings zusr tid newService
createSettings ::
- Member TeamStore r =>
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
UserId ->
TeamId ->
Public.NewLegalHoldService ->
@@ -124,18 +125,18 @@ createSettings zusr tid newService = do
>>= maybe (throwM legalHoldServiceInvalidKey) pure
LHService.checkLegalHoldServiceStatus fpr (newLegalHoldServiceUrl newService)
let service = legalHoldService tid fpr newService key
- LegalHoldData.createSettings service
+ liftSem $ LegalHoldData.createSettings service
pure . viewLegalHoldService $ service
getSettingsH ::
- Member TeamStore r =>
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
UserId ::: TeamId ::: JSON ->
Galley r Response
getSettingsH (zusr ::: tid ::: _) = do
json <$> getSettings zusr tid
getSettings ::
- Member TeamStore r =>
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
UserId ->
TeamId ->
Galley r Public.ViewLegalHoldService
@@ -143,7 +144,7 @@ getSettings zusr tid = do
zusrMembership <- liftSem $ getTeamMember tid zusr
void $ permissionCheck (ViewTeamFeature Public.TeamFeatureLegalHold) zusrMembership
isenabled <- isLegalHoldEnabledForTeam tid
- mresult <- LegalHoldData.getSettings tid
+ mresult <- liftSem $ LegalHoldData.getSettings tid
pure $ case (isenabled, mresult) of
(False, _) -> Public.ViewLegalHoldServiceDisabled
(True, Nothing) -> Public.ViewLegalHoldServiceNotConfigured
@@ -159,9 +160,11 @@ removeSettingsH ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore,
+ TeamFeatureStore,
TeamMemberStore InternalPaging
]
r =>
@@ -184,8 +187,10 @@ removeSettings ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
+ TeamFeatureStore,
TeamStore,
TeamMemberStore p
]
@@ -229,6 +234,7 @@ removeSettings' ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore,
@@ -260,11 +266,19 @@ removeSettings' tid =
-- | Learn whether a user has LH enabled and fetch pre-keys.
-- Note that this is accessible to ANY authenticated user, even ones outside the team
-getUserStatusH :: Member TeamStore r => UserId ::: TeamId ::: UserId ::: JSON -> Galley r Response
+getUserStatusH ::
+ Members '[LegalHoldStore, TeamStore] r =>
+ UserId ::: TeamId ::: UserId ::: JSON ->
+ Galley r Response
getUserStatusH (_zusr ::: tid ::: uid ::: _) = do
json <$> getUserStatus tid uid
-getUserStatus :: Member TeamStore r => TeamId -> UserId -> Galley r Public.UserLegalHoldStatusResponse
+getUserStatus ::
+ forall r.
+ Members '[LegalHoldStore, TeamStore] r =>
+ TeamId ->
+ UserId ->
+ Galley r Public.UserLegalHoldStatusResponse
getUserStatus tid uid = do
mTeamMember <- liftSem $ getTeamMember tid uid
teamMember <- maybe (throwM teamMemberNotFound) pure mTeamMember
@@ -278,7 +292,7 @@ getUserStatus tid uid = do
where
makeResponseDetails :: Galley r (Maybe LastPrekey, Maybe ClientId)
makeResponseDetails = do
- mLastKey <- fmap snd <$> LegalHoldData.selectPendingPrekeys uid
+ mLastKey <- liftSem $ fmap snd <$> LegalHoldData.selectPendingPrekeys uid
lastKey <- case mLastKey of
Nothing -> do
Log.err . Log.msg $
@@ -303,6 +317,7 @@ grantConsentH ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore
@@ -329,6 +344,7 @@ grantConsent ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore
@@ -361,8 +377,10 @@ requestDeviceH ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
+ TeamFeatureStore,
TeamStore
]
r =>
@@ -388,8 +406,10 @@ requestDevice ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
+ TeamFeatureStore,
TeamStore
]
r =>
@@ -422,13 +442,13 @@ requestDevice zusr tid uid = do
provisionLHDevice userLHStatus = do
(lastPrekey', prekeys) <- requestDeviceFromService
-- We don't distinguish the last key here; brig will do so when the device is added
- LegalHoldData.insertPendingPrekeys uid (unpackLastPrekey lastPrekey' : prekeys)
+ liftSem $ LegalHoldData.insertPendingPrekeys uid (unpackLastPrekey lastPrekey' : prekeys)
changeLegalholdStatus tid uid userLHStatus UserLegalHoldPending
liftSem $ notifyClientsAboutLegalHoldRequest zusr uid lastPrekey'
requestDeviceFromService :: Galley r (LastPrekey, [Prekey])
requestDeviceFromService = do
- LegalHoldData.dropPendingPrekeys uid
+ liftSem $ LegalHoldData.dropPendingPrekeys uid
lhDevice <- LHService.requestNewDevice tid uid
let NewLegalHoldClient prekeys lastKey = lhDevice
return (lastKey, prekeys)
@@ -448,8 +468,10 @@ approveDeviceH ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
+ TeamFeatureStore,
TeamStore
]
r =>
@@ -470,8 +492,10 @@ approveDevice ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
+ TeamFeatureStore,
TeamStore
]
r =>
@@ -493,7 +517,7 @@ approveDevice zusr tid uid connId (Public.ApproveLegalHoldForUserRequest mPasswo
liftSem $
maybe defUserLegalHoldStatus (view legalHoldStatus) <$> getTeamMember tid uid
assertUserLHPending userLHStatus
- mPreKeys <- LegalHoldData.selectPendingPrekeys uid
+ mPreKeys <- liftSem $ LegalHoldData.selectPendingPrekeys uid
(prekeys, lastPrekey') <- case mPreKeys of
Nothing -> do
Log.info $ Log.msg @Text "No prekeys found"
@@ -529,6 +553,7 @@ disableForUserH ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore
@@ -557,6 +582,7 @@ disableForUser ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore
@@ -604,6 +630,7 @@ changeLegalholdStatus ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore
@@ -619,25 +646,25 @@ changeLegalholdStatus tid uid old new = do
UserLegalHoldEnabled -> case new of
UserLegalHoldEnabled -> noop
UserLegalHoldPending -> illegal
- UserLegalHoldDisabled -> update >> removeblocks
+ UserLegalHoldDisabled -> liftSem update >> removeblocks
UserLegalHoldNoConsent -> illegal
--
UserLegalHoldPending -> case new of
- UserLegalHoldEnabled -> update
+ UserLegalHoldEnabled -> liftSem update
UserLegalHoldPending -> noop
- UserLegalHoldDisabled -> update >> removeblocks
+ UserLegalHoldDisabled -> liftSem update >> removeblocks
UserLegalHoldNoConsent -> illegal
--
UserLegalHoldDisabled -> case new of
UserLegalHoldEnabled -> illegal
- UserLegalHoldPending -> addblocks >> update
+ UserLegalHoldPending -> addblocks >> liftSem update
UserLegalHoldDisabled -> {- in case the last attempt crashed -} removeblocks
UserLegalHoldNoConsent -> {- withdrawing consent is not (yet?) implemented -} illegal
--
UserLegalHoldNoConsent -> case new of
UserLegalHoldEnabled -> illegal
UserLegalHoldPending -> illegal
- UserLegalHoldDisabled -> update
+ UserLegalHoldDisabled -> liftSem update
UserLegalHoldNoConsent -> noop
where
update = LegalHoldData.setUserLegalHoldStatus tid uid new
@@ -651,7 +678,7 @@ changeLegalholdStatus tid uid old new = do
-- FUTUREWORK: make this async?
blockNonConsentingConnections ::
forall r.
- Members '[BrigAccess, TeamStore] r =>
+ Members '[BrigAccess, LegalHoldStore, TeamStore] r =>
UserId ->
Galley r ()
blockNonConsentingConnections uid = do
@@ -679,19 +706,21 @@ blockNonConsentingConnections uid = do
status <- liftSem $ putConnectionInternal (BlockForMissingLHConsent userLegalhold othersToBlock)
pure $ ["blocking users failed: " <> show (status, othersToBlock) | status /= status200]
-setTeamLegalholdWhitelisted :: TeamId -> Galley r ()
-setTeamLegalholdWhitelisted tid = do
- LegalHoldData.setTeamLegalholdWhitelisted tid
+setTeamLegalholdWhitelisted :: Member LegalHoldStore r => TeamId -> Galley r ()
+setTeamLegalholdWhitelisted tid =
+ liftSem $
+ LegalHoldData.setTeamLegalholdWhitelisted tid
-setTeamLegalholdWhitelistedH :: TeamId -> Galley r Response
+setTeamLegalholdWhitelistedH :: Member LegalHoldStore r => TeamId -> Galley r Response
setTeamLegalholdWhitelistedH tid = do
empty <$ setTeamLegalholdWhitelisted tid
-unsetTeamLegalholdWhitelisted :: TeamId -> Galley r ()
-unsetTeamLegalholdWhitelisted tid = do
- LegalHoldData.unsetTeamLegalholdWhitelisted tid
+unsetTeamLegalholdWhitelisted :: Member LegalHoldStore r => TeamId -> Galley r ()
+unsetTeamLegalholdWhitelisted tid =
+ liftSem $
+ LegalHoldData.unsetTeamLegalholdWhitelisted tid
-unsetTeamLegalholdWhitelistedH :: TeamId -> Galley r Response
+unsetTeamLegalholdWhitelistedH :: Member LegalHoldStore r => TeamId -> Galley r Response
unsetTeamLegalholdWhitelistedH tid = do
() <-
error
@@ -700,9 +729,9 @@ unsetTeamLegalholdWhitelistedH tid = do
\before you enable the end-point."
setStatus status204 empty <$ unsetTeamLegalholdWhitelisted tid
-getTeamLegalholdWhitelistedH :: TeamId -> Galley r Response
-getTeamLegalholdWhitelistedH tid = do
- lhEnabled <- isTeamLegalholdWhitelisted tid
+getTeamLegalholdWhitelistedH :: Member LegalHoldStore r => TeamId -> Galley r Response
+getTeamLegalholdWhitelistedH tid = liftSem $ do
+ lhEnabled <- LegalHoldData.isTeamLegalholdWhitelisted tid
pure $
if lhEnabled
then setStatus status200 empty
@@ -732,6 +761,7 @@ handleGroupConvPolicyConflicts ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
TeamStore
diff --git a/services/galley/src/Galley/API/Query.hs b/services/galley/src/Galley/API/Query.hs
index 6b07b1d18f..d395b1e424 100644
--- a/services/galley/src/Galley/API/Query.hs
+++ b/services/galley/src/Galley/API/Query.hs
@@ -52,7 +52,6 @@ import qualified Galley.API.Mapping as Mapping
import Galley.API.Util
import Galley.App
import Galley.Cassandra.Paging
-import Galley.Data.ResultSet
import qualified Galley.Data.Types as Data
import Galley.Effects
import qualified Galley.Effects.ConversationStore as E
diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs
index 8c32af600c..0b58052ecc 100644
--- a/services/galley/src/Galley/API/Teams.hs
+++ b/services/galley/src/Galley/API/Teams.hs
@@ -88,20 +88,19 @@ import Galley.API.Util
import Galley.App
import Galley.Cassandra.Paging
import qualified Galley.Data.Conversation as Data
-import qualified Galley.Data.LegalHold as Data
-import qualified Galley.Data.ResultSet as Data
-import qualified Galley.Data.SearchVisibility as SearchVisibilityData
import Galley.Data.Services (BotMember)
-import qualified Galley.Data.TeamFeatures as TeamFeatures
import Galley.Effects
import qualified Galley.Effects.BrigAccess as E
import qualified Galley.Effects.ConversationStore as E
import qualified Galley.Effects.ExternalAccess as E
import qualified Galley.Effects.GundeckAccess as E
+import qualified Galley.Effects.LegalHoldStore as Data
import qualified Galley.Effects.ListItems as E
import qualified Galley.Effects.MemberStore as E
import qualified Galley.Effects.Paging as E
+import qualified Galley.Effects.SearchVisibilityStore as SearchVisibilityData
import qualified Galley.Effects.SparAccess as Spar
+import qualified Galley.Effects.TeamFeatureStore as TeamFeatures
import qualified Galley.Effects.TeamMemberStore as E
import qualified Galley.Effects.TeamStore as E
import qualified Galley.Intra.Journal as Journal
@@ -367,6 +366,7 @@ uncheckedDeleteTeam ::
'[ BrigAccess,
ExternalAccess,
GundeckAccess,
+ LegalHoldStore,
MemberStore,
SparAccess,
TeamStore
@@ -400,7 +400,7 @@ uncheckedDeleteTeam zusr zcon tid = do
when ((view teamBinding . tdTeam <$> team) == Just Binding) $ do
liftSem $ mapM_ (E.deleteUser . view userId) membs
Journal.teamDelete tid
- Data.unsetTeamLegalholdWhitelisted tid
+ liftSem $ Data.unsetTeamLegalholdWhitelisted tid
liftSem $ E.deleteTeam tid
where
pushDeleteEvents :: [TeamMember] -> Event -> [Push] -> Galley r ()
@@ -687,7 +687,16 @@ uncheckedGetTeamMembers ::
uncheckedGetTeamMembers tid maxResults = liftSem $ E.getTeamMembersWithLimit tid maxResults
addTeamMemberH ::
- Members '[BrigAccess, GundeckAccess, MemberStore, TeamStore] r =>
+ Members
+ '[ BrigAccess,
+ GundeckAccess,
+ LegalHoldStore,
+ MemberStore,
+ TeamFeatureStore,
+ TeamNotificationStore,
+ TeamStore
+ ]
+ r =>
UserId ::: ConnId ::: TeamId ::: JsonRequest Public.NewTeamMember ::: JSON ->
Galley r Response
addTeamMemberH (zusr ::: zcon ::: tid ::: req ::: _) = do
@@ -696,7 +705,16 @@ addTeamMemberH (zusr ::: zcon ::: tid ::: req ::: _) = do
pure empty
addTeamMember ::
- Members '[BrigAccess, GundeckAccess, MemberStore, TeamStore] r =>
+ Members
+ '[ BrigAccess,
+ GundeckAccess,
+ LegalHoldStore,
+ MemberStore,
+ TeamFeatureStore,
+ TeamNotificationStore,
+ TeamStore
+ ]
+ r =>
UserId ->
ConnId ->
TeamId ->
@@ -723,7 +741,16 @@ addTeamMember zusr zcon tid nmem = do
-- This function is "unchecked" because there is no need to check for user binding (invite only).
uncheckedAddTeamMemberH ::
- Members '[BrigAccess, GundeckAccess, MemberStore, TeamStore] r =>
+ Members
+ '[ BrigAccess,
+ GundeckAccess,
+ MemberStore,
+ LegalHoldStore,
+ TeamFeatureStore,
+ TeamStore,
+ TeamNotificationStore
+ ]
+ r =>
TeamId ::: JsonRequest NewTeamMember ::: JSON ->
Galley r Response
uncheckedAddTeamMemberH (tid ::: req ::: _) = do
@@ -732,7 +759,16 @@ uncheckedAddTeamMemberH (tid ::: req ::: _) = do
return empty
uncheckedAddTeamMember ::
- Members '[BrigAccess, GundeckAccess, MemberStore, TeamStore] r =>
+ Members
+ '[ BrigAccess,
+ GundeckAccess,
+ MemberStore,
+ LegalHoldStore,
+ TeamFeatureStore,
+ TeamStore,
+ TeamNotificationStore
+ ]
+ r =>
TeamId ->
NewTeamMember ->
Galley r ()
@@ -994,6 +1030,7 @@ deleteTeamConversation ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
MemberStore,
TeamStore
]
@@ -1009,7 +1046,7 @@ deleteTeamConversation zusr zcon _tid cid = do
void $ API.deleteLocalConversation lusr zcon lconv
getSearchVisibilityH ::
- Member TeamStore r =>
+ Members '[SearchVisibilityStore, TeamStore] r =>
UserId ::: TeamId ::: JSON ->
Galley r Response
getSearchVisibilityH (uid ::: tid ::: _) = do
@@ -1018,7 +1055,7 @@ getSearchVisibilityH (uid ::: tid ::: _) = do
json <$> getSearchVisibilityInternal tid
setSearchVisibilityH ::
- Member TeamStore r =>
+ Members '[SearchVisibilityStore, TeamStore, TeamFeatureStore] r =>
UserId ::: TeamId ::: JsonRequest Public.TeamSearchVisibilityView ::: JSON ->
Galley r Response
setSearchVisibilityH (uid ::: tid ::: req ::: _) = do
@@ -1051,10 +1088,10 @@ withTeamIds ::
withTeamIds usr range size k = case range of
Nothing -> do
r <- liftSem $ E.listItems usr Nothing (rcast size)
- k (Data.resultSetType r == Data.ResultSetTruncated) (Data.resultSetResult r)
+ k (resultSetType r == ResultSetTruncated) (resultSetResult r)
Just (Right c) -> do
r <- liftSem $ E.listItems usr (Just c) (rcast size)
- k (Data.resultSetType r == Data.ResultSetTruncated) (Data.resultSetResult r)
+ k (resultSetType r == ResultSetTruncated) (resultSetResult r)
Just (Left (fromRange -> cc)) -> do
ids <- liftSem $ E.selectTeams usr (Data.ByteString.Conversion.fromList cc)
k False ids
@@ -1102,7 +1139,11 @@ ensureNotTooLarge tid = do
-- size unlimited, because we make the assumption that these teams won't turn
-- LegalHold off after activation.
-- FUTUREWORK: Find a way around the fanout limit.
-ensureNotTooLargeForLegalHold :: Member BrigAccess r => TeamId -> Int -> Galley r ()
+ensureNotTooLargeForLegalHold ::
+ Members '[BrigAccess, LegalHoldStore, TeamFeatureStore] r =>
+ TeamId ->
+ Int ->
+ Galley r ()
ensureNotTooLargeForLegalHold tid teamSize = do
whenM (isLegalHoldEnabledForTeam tid) $ do
unlessM (teamSizeBelowLimit teamSize) $ do
@@ -1126,7 +1167,7 @@ teamSizeBelowLimit teamSize = do
pure True
addTeamMemberInternal ::
- Members '[BrigAccess, GundeckAccess, MemberStore, TeamStore] r =>
+ Members '[BrigAccess, GundeckAccess, MemberStore, TeamNotificationStore, TeamStore] r =>
TeamId ->
Maybe UserId ->
Maybe ConnId ->
@@ -1164,7 +1205,7 @@ addTeamMemberInternal tid origin originConn (view ntmNewTeamMember -> new) memLi
-- less warped. This is a work-around because we cannot send events to all of a large team.
-- See haddocks of module "Galley.API.TeamNotifications" for details.
getTeamNotificationsH ::
- Member BrigAccess r =>
+ Members '[BrigAccess, TeamNotificationStore] r =>
UserId
::: Maybe ByteString {- NotificationId -}
::: Range 1 10000 Int32
@@ -1226,18 +1267,24 @@ getBindingTeamMembers :: Member TeamStore r => UserId -> Galley r TeamMemberList
getBindingTeamMembers zusr = withBindingTeam zusr $ \tid ->
getTeamMembersForFanout tid
-canUserJoinTeamH :: Member BrigAccess r => TeamId -> Galley r Response
+canUserJoinTeamH ::
+ Members '[BrigAccess, LegalHoldStore, TeamFeatureStore] r =>
+ TeamId ->
+ Galley r Response
canUserJoinTeamH tid = canUserJoinTeam tid >> pure empty
-- This could be extended for more checks, for now we test only legalhold
-canUserJoinTeam :: Member BrigAccess r => TeamId -> Galley r ()
+canUserJoinTeam :: Members '[BrigAccess, LegalHoldStore, TeamFeatureStore] r => TeamId -> Galley r ()
canUserJoinTeam tid = do
lhEnabled <- isLegalHoldEnabledForTeam tid
when lhEnabled $ do
(TeamSize sizeBeforeJoin) <- liftSem $ E.getSize tid
ensureNotTooLargeForLegalHold tid (fromIntegral sizeBeforeJoin + 1)
-getTeamSearchVisibilityAvailableInternal :: TeamId -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSearchVisibility)
+getTeamSearchVisibilityAvailableInternal ::
+ Member TeamFeatureStore r =>
+ TeamId ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSearchVisibility)
getTeamSearchVisibilityAvailableInternal tid = do
-- TODO: This is just redundant given there is a decent default
defConfig <- do
@@ -1246,28 +1293,44 @@ getTeamSearchVisibilityAvailableInternal tid = do
FeatureTeamSearchVisibilityEnabledByDefault -> Public.TeamFeatureEnabled
FeatureTeamSearchVisibilityDisabledByDefault -> Public.TeamFeatureDisabled
- fromMaybe defConfig
- <$> TeamFeatures.getFeatureStatusNoConfig @'Public.TeamFeatureSearchVisibility tid
+ liftSem $
+ fromMaybe defConfig
+ <$> TeamFeatures.getFeatureStatusNoConfig @'Public.TeamFeatureSearchVisibility tid
-- | Modify and get visibility type for a team (internal, no user permission checks)
-getSearchVisibilityInternalH :: TeamId ::: JSON -> Galley r Response
+getSearchVisibilityInternalH ::
+ Member SearchVisibilityStore r =>
+ TeamId ::: JSON ->
+ Galley r Response
getSearchVisibilityInternalH (tid ::: _) =
json <$> getSearchVisibilityInternal tid
-getSearchVisibilityInternal :: TeamId -> Galley r TeamSearchVisibilityView
-getSearchVisibilityInternal = fmap TeamSearchVisibilityView . SearchVisibilityData.getSearchVisibility
-
-setSearchVisibilityInternalH :: TeamId ::: JsonRequest TeamSearchVisibilityView ::: JSON -> Galley r Response
+getSearchVisibilityInternal ::
+ Member SearchVisibilityStore r =>
+ TeamId ->
+ Galley r TeamSearchVisibilityView
+getSearchVisibilityInternal =
+ fmap TeamSearchVisibilityView . liftSem
+ . SearchVisibilityData.getSearchVisibility
+
+setSearchVisibilityInternalH ::
+ Members '[SearchVisibilityStore, TeamFeatureStore] r =>
+ TeamId ::: JsonRequest TeamSearchVisibilityView ::: JSON ->
+ Galley r Response
setSearchVisibilityInternalH (tid ::: req ::: _) = do
setSearchVisibilityInternal tid =<< fromJsonBody req
pure noContent
-setSearchVisibilityInternal :: TeamId -> TeamSearchVisibilityView -> Galley r ()
+setSearchVisibilityInternal ::
+ Members '[SearchVisibilityStore, TeamFeatureStore] r =>
+ TeamId ->
+ TeamSearchVisibilityView ->
+ Galley r ()
setSearchVisibilityInternal tid (TeamSearchVisibilityView searchVisibility) = do
status <- getTeamSearchVisibilityAvailableInternal tid
unless (Public.tfwoStatus status == Public.TeamFeatureEnabled) $
throwM teamSearchVisibilityNotEnabled
- SearchVisibilityData.setSearchVisibility tid searchVisibility
+ liftSem $ SearchVisibilityData.setSearchVisibility tid searchVisibility
userIsTeamOwnerH :: Member TeamStore r => TeamId ::: UserId ::: JSON -> Galley r Response
userIsTeamOwnerH (tid ::: uid ::: _) = do
diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs
index f3dcd40448..4204906747 100644
--- a/services/galley/src/Galley/API/Teams/Features.hs
+++ b/services/galley/src/Galley/API/Teams/Features.hs
@@ -60,11 +60,12 @@ import Galley.API.Teams (ensureNotTooLargeToActivateLegalHold)
import Galley.API.Util
import Galley.App
import Galley.Cassandra.Paging
-import qualified Galley.Data.SearchVisibility as SearchVisibilityData
-import qualified Galley.Data.TeamFeatures as TeamFeatures
+import Galley.Data.TeamFeatures
import Galley.Effects
import Galley.Effects.GundeckAccess
import Galley.Effects.Paging
+import qualified Galley.Effects.SearchVisibilityStore as SearchVisibilityData
+import qualified Galley.Effects.TeamFeatureStore as TeamFeatures
import Galley.Effects.TeamStore
import Galley.Intra.Push (PushEvent (FeatureConfigEvent), newPush)
import Galley.Options
@@ -139,7 +140,10 @@ getFeatureConfig getter zusr = do
assertTeamExists tid
getter (Right tid)
-getAllFeatureConfigs :: Member TeamStore r => UserId -> Galley r AllFeatureConfigs
+getAllFeatureConfigs ::
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
+ UserId ->
+ Galley r AllFeatureConfigs
getAllFeatureConfigs zusr = do
mbTeam <- liftSem $ getOneUserTeam zusr
zusrMembership <- maybe (pure Nothing) (liftSem . (flip getTeamMember zusr)) mbTeam
@@ -172,11 +176,19 @@ getAllFeatureConfigs zusr = do
getStatus @'Public.TeamFeatureSelfDeletingMessages getSelfDeletingMessagesInternal
]
-getAllFeaturesH :: Member TeamStore r => UserId ::: TeamId ::: JSON -> Galley r Response
+getAllFeaturesH ::
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
+ UserId ::: TeamId ::: JSON ->
+ Galley r Response
getAllFeaturesH (uid ::: tid ::: _) =
json <$> getAllFeatures uid tid
-getAllFeatures :: forall r. Member TeamStore r => UserId -> TeamId -> Galley r Aeson.Value
+getAllFeatures ::
+ forall r.
+ Members '[LegalHoldStore, TeamFeatureStore, TeamStore] r =>
+ UserId ->
+ TeamId ->
+ Galley r Aeson.Value
getAllFeatures uid tid = do
Aeson.object
<$> sequence
@@ -206,20 +218,24 @@ getAllFeatures uid tid = do
getFeatureStatusNoConfig ::
forall (a :: Public.TeamFeatureName) r.
- (Public.KnownTeamFeatureName a, Public.FeatureHasNoConfig a, TeamFeatures.HasStatusCol a) =>
+ ( Public.KnownTeamFeatureName a,
+ Public.FeatureHasNoConfig a,
+ HasStatusCol a,
+ Member TeamFeatureStore r
+ ) =>
Galley r Public.TeamFeatureStatusValue ->
TeamId ->
Galley r (Public.TeamFeatureStatus a)
getFeatureStatusNoConfig getDefault tid = do
defaultStatus <- Public.TeamFeatureStatusNoConfig <$> getDefault
- fromMaybe defaultStatus <$> TeamFeatures.getFeatureStatusNoConfig @a tid
+ liftSem $ fromMaybe defaultStatus <$> TeamFeatures.getFeatureStatusNoConfig @a tid
setFeatureStatusNoConfig ::
forall (a :: Public.TeamFeatureName) r.
( Public.KnownTeamFeatureName a,
Public.FeatureHasNoConfig a,
- TeamFeatures.HasStatusCol a,
- Members '[GundeckAccess, TeamStore] r
+ HasStatusCol a,
+ Members '[GundeckAccess, TeamFeatureStore, TeamStore] r
) =>
(Public.TeamFeatureStatusValue -> TeamId -> Galley r ()) ->
TeamId ->
@@ -227,7 +243,7 @@ setFeatureStatusNoConfig ::
Galley r (Public.TeamFeatureStatus a)
setFeatureStatusNoConfig applyState tid status = do
applyState (Public.tfwoStatus status) tid
- newStatus <- TeamFeatures.setFeatureStatusNoConfig @a tid status
+ newStatus <- liftSem $ TeamFeatures.setFeatureStatusNoConfig @a tid status
pushFeatureConfigEvent tid $
Event.Event Event.Update (Public.knownTeamFeatureName @a) (EdFeatureWithoutConfigChanged newStatus)
pure newStatus
@@ -236,7 +252,10 @@ setFeatureStatusNoConfig applyState tid status = do
-- the feature flag, so that we get more type safety.
type GetFeatureInternalParam = Either (Maybe UserId) TeamId
-getSSOStatusInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSSO)
+getSSOStatusInternal ::
+ Member TeamFeatureStore r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSSO)
getSSOStatusInternal =
either
(const $ Public.TeamFeatureStatusNoConfig <$> getDef)
@@ -249,7 +268,7 @@ getSSOStatusInternal =
FeatureSSODisabledByDefault -> Public.TeamFeatureDisabled
setSSOStatusInternal ::
- Members '[GundeckAccess, TeamStore] r =>
+ Members '[GundeckAccess, TeamFeatureStore, TeamStore] r =>
TeamId ->
(Public.TeamFeatureStatus 'Public.TeamFeatureSSO) ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSSO)
@@ -257,7 +276,10 @@ setSSOStatusInternal = setFeatureStatusNoConfig @'Public.TeamFeatureSSO $ \case
Public.TeamFeatureDisabled -> const (throwM disableSsoNotImplemented)
Public.TeamFeatureEnabled -> const (pure ())
-getTeamSearchVisibilityAvailableInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSearchVisibility)
+getTeamSearchVisibilityAvailableInternal ::
+ Member TeamFeatureStore r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSearchVisibility)
getTeamSearchVisibilityAvailableInternal =
either
(const $ Public.TeamFeatureStatusNoConfig <$> getDef)
@@ -269,15 +291,18 @@ getTeamSearchVisibilityAvailableInternal =
FeatureTeamSearchVisibilityDisabledByDefault -> Public.TeamFeatureDisabled
setTeamSearchVisibilityAvailableInternal ::
- Members '[GundeckAccess, TeamStore] r =>
+ Members '[GundeckAccess, SearchVisibilityStore, TeamFeatureStore, TeamStore] r =>
TeamId ->
(Public.TeamFeatureStatus 'Public.TeamFeatureSearchVisibility) ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSearchVisibility)
setTeamSearchVisibilityAvailableInternal = setFeatureStatusNoConfig @'Public.TeamFeatureSearchVisibility $ \case
- Public.TeamFeatureDisabled -> SearchVisibilityData.resetSearchVisibility
+ Public.TeamFeatureDisabled -> liftSem . SearchVisibilityData.resetSearchVisibility
Public.TeamFeatureEnabled -> const (pure ())
-getValidateSAMLEmailsInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureValidateSAMLEmails)
+getValidateSAMLEmailsInternal ::
+ Member TeamFeatureStore r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureValidateSAMLEmails)
getValidateSAMLEmailsInternal =
either
(const $ Public.TeamFeatureStatusNoConfig <$> getDef)
@@ -289,13 +314,16 @@ getValidateSAMLEmailsInternal =
getDef = pure Public.TeamFeatureDisabled
setValidateSAMLEmailsInternal ::
- Members '[GundeckAccess, TeamStore] r =>
+ Members '[GundeckAccess, TeamFeatureStore, TeamStore] r =>
TeamId ->
(Public.TeamFeatureStatus 'Public.TeamFeatureValidateSAMLEmails) ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureValidateSAMLEmails)
setValidateSAMLEmailsInternal = setFeatureStatusNoConfig @'Public.TeamFeatureValidateSAMLEmails $ \_ _ -> pure ()
-getDigitalSignaturesInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureDigitalSignatures)
+getDigitalSignaturesInternal ::
+ Member TeamFeatureStore r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureDigitalSignatures)
getDigitalSignaturesInternal =
either
(const $ Public.TeamFeatureStatusNoConfig <$> getDef)
@@ -307,13 +335,16 @@ getDigitalSignaturesInternal =
getDef = pure Public.TeamFeatureDisabled
setDigitalSignaturesInternal ::
- Members '[GundeckAccess, TeamStore] r =>
+ Members '[GundeckAccess, TeamFeatureStore, TeamStore] r =>
TeamId ->
Public.TeamFeatureStatus 'Public.TeamFeatureDigitalSignatures ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureDigitalSignatures)
setDigitalSignaturesInternal = setFeatureStatusNoConfig @'Public.TeamFeatureDigitalSignatures $ \_ _ -> pure ()
-getLegalholdStatusInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureLegalHold)
+getLegalholdStatusInternal ::
+ Members '[LegalHoldStore, TeamFeatureStore] r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureLegalHold)
getLegalholdStatusInternal (Left _) =
pure $ Public.TeamFeatureStatusNoConfig Public.TeamFeatureDisabled
getLegalholdStatusInternal (Right tid) = do
@@ -333,8 +364,10 @@ setLegalholdStatusInternal ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
ListItems LegacyPaging ConvId,
MemberStore,
+ TeamFeatureStore,
TeamStore,
TeamMemberStore p
]
@@ -362,15 +395,18 @@ setLegalholdStatusInternal tid status@(Public.tfwoStatus -> statusValue) = do
Public.TeamFeatureDisabled -> removeSettings' tid
Public.TeamFeatureEnabled -> do
ensureNotTooLargeToActivateLegalHold tid
- TeamFeatures.setFeatureStatusNoConfig @'Public.TeamFeatureLegalHold tid status
+ liftSem $ TeamFeatures.setFeatureStatusNoConfig @'Public.TeamFeatureLegalHold tid status
-getFileSharingInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureFileSharing)
+getFileSharingInternal ::
+ Member TeamFeatureStore r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureFileSharing)
getFileSharingInternal =
getFeatureStatusWithDefaultConfig @'Public.TeamFeatureFileSharing flagFileSharing . either (const Nothing) Just
getFeatureStatusWithDefaultConfig ::
forall (a :: TeamFeatureName) r.
- (KnownTeamFeatureName a, TeamFeatures.HasStatusCol a, FeatureHasNoConfig a) =>
+ (KnownTeamFeatureName a, HasStatusCol a, FeatureHasNoConfig a, Member TeamFeatureStore r) =>
Lens' FeatureFlags (Defaults (Public.TeamFeatureStatus a)) ->
Maybe TeamId ->
Galley r (Public.TeamFeatureStatus a)
@@ -385,23 +421,32 @@ getFeatureStatusWithDefaultConfig lens' =
<&> Public.tfwoStatus . view unDefaults
setFileSharingInternal ::
- Members '[GundeckAccess, TeamStore] r =>
+ Members '[GundeckAccess, TeamFeatureStore, TeamStore] r =>
TeamId ->
Public.TeamFeatureStatus 'Public.TeamFeatureFileSharing ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureFileSharing)
setFileSharingInternal = setFeatureStatusNoConfig @'Public.TeamFeatureFileSharing $ \_status _tid -> pure ()
-getAppLockInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureAppLock)
+getAppLockInternal ::
+ Member TeamFeatureStore r =>
+ GetFeatureInternalParam ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureAppLock)
getAppLockInternal mbtid = do
Defaults defaultStatus <- view (options . optSettings . setFeatureFlags . flagAppLockDefaults)
- status <- join <$> (TeamFeatures.getApplockFeatureStatus `mapM` either (const Nothing) Just mbtid)
+ status <-
+ liftSem $
+ join <$> (TeamFeatures.getApplockFeatureStatus `mapM` either (const Nothing) Just mbtid)
pure $ fromMaybe defaultStatus status
-setAppLockInternal :: TeamId -> Public.TeamFeatureStatus 'Public.TeamFeatureAppLock -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureAppLock)
+setAppLockInternal ::
+ Member TeamFeatureStore r =>
+ TeamId ->
+ Public.TeamFeatureStatus 'Public.TeamFeatureAppLock ->
+ Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureAppLock)
setAppLockInternal tid status = do
when (Public.applockInactivityTimeoutSecs (Public.tfwcConfig status) < 30) $
throwM inactivityTimeoutTooLow
- TeamFeatures.setApplockFeatureStatus tid status
+ liftSem $ TeamFeatures.setApplockFeatureStatus tid status
getClassifiedDomainsInternal :: GetFeatureInternalParam -> Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureClassifiedDomains)
getClassifiedDomainsInternal _mbtid = do
@@ -413,6 +458,7 @@ getClassifiedDomainsInternal _mbtid = do
Public.TeamFeatureEnabled -> config
getConferenceCallingInternal ::
+ Member TeamFeatureStore r =>
GetFeatureInternalParam ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureConferenceCalling)
getConferenceCallingInternal (Left (Just uid)) = do
@@ -423,26 +469,32 @@ getConferenceCallingInternal (Right tid) = do
getFeatureStatusWithDefaultConfig @'Public.TeamFeatureConferenceCalling flagConferenceCalling (Just tid)
setConferenceCallingInternal ::
- Members '[GundeckAccess, TeamStore] r =>
+ Members '[GundeckAccess, TeamFeatureStore, TeamStore] r =>
TeamId ->
Public.TeamFeatureStatus 'Public.TeamFeatureConferenceCalling ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureConferenceCalling)
-setConferenceCallingInternal = setFeatureStatusNoConfig @'Public.TeamFeatureConferenceCalling $ \_status _tid -> pure ()
+setConferenceCallingInternal =
+ setFeatureStatusNoConfig @'Public.TeamFeatureConferenceCalling $ \_status _tid -> pure ()
getSelfDeletingMessagesInternal ::
+ Member TeamFeatureStore r =>
GetFeatureInternalParam ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSelfDeletingMessages)
getSelfDeletingMessagesInternal = \case
Left _ -> pure Public.defaultSelfDeletingMessagesStatus
Right tid ->
- TeamFeatures.getSelfDeletingMessagesStatus tid
- <&> maybe Public.defaultSelfDeletingMessagesStatus id
+ liftSem $
+ TeamFeatures.getSelfDeletingMessagesStatus tid
+ <&> maybe Public.defaultSelfDeletingMessagesStatus id
setSelfDeletingMessagesInternal ::
+ Member TeamFeatureStore r =>
TeamId ->
Public.TeamFeatureStatus 'Public.TeamFeatureSelfDeletingMessages ->
Galley r (Public.TeamFeatureStatus 'Public.TeamFeatureSelfDeletingMessages)
-setSelfDeletingMessagesInternal = TeamFeatures.setSelfDeletingMessagesStatus
+setSelfDeletingMessagesInternal tid value =
+ liftSem $
+ TeamFeatures.setSelfDeletingMessagesStatus tid value
pushFeatureConfigEvent ::
Members '[GundeckAccess, TeamStore] r =>
diff --git a/services/galley/src/Galley/API/Teams/Notifications.hs b/services/galley/src/Galley/API/Teams/Notifications.hs
index d37e42a64b..8b4b0117a1 100644
--- a/services/galley/src/Galley/API/Teams/Notifications.hs
+++ b/services/galley/src/Galley/API/Teams/Notifications.hs
@@ -53,6 +53,7 @@ import Galley.App
import qualified Galley.Data.TeamNotifications as DataTeamQueue
import Galley.Effects
import Galley.Effects.BrigAccess as Intra
+import qualified Galley.Effects.TeamNotificationStore as E
import Galley.Types.Teams hiding (newTeam)
import Gundeck.Types.Notification
import Imports
@@ -60,7 +61,7 @@ import Network.HTTP.Types
import Network.Wai.Utilities
getTeamNotifications ::
- Member BrigAccess r =>
+ Members '[BrigAccess, TeamNotificationStore] r =>
UserId ->
Maybe NotificationId ->
Range 1 10000 Int32 ->
@@ -70,17 +71,17 @@ getTeamNotifications zusr since size = do
mtid <- liftSem $ (userTeam . accountUser =<<) <$> Intra.getUser zusr
let err = throwM teamNotFound
maybe err pure mtid
- page <- DataTeamQueue.fetch tid since size
+ page <- liftSem $ E.getTeamNotifications tid since size
pure $
queuedNotificationList
(toList (DataTeamQueue.resultSeq page))
(DataTeamQueue.resultHasMore page)
Nothing
-pushTeamEvent :: TeamId -> Event -> Galley r ()
+pushTeamEvent :: Member TeamNotificationStore r => TeamId -> Event -> Galley r ()
pushTeamEvent tid evt = do
nid <- mkNotificationId
- DataTeamQueue.add tid nid (List1.singleton $ toJSONObject evt)
+ liftSem $ E.createTeamNotification tid nid (List1.singleton $ toJSONObject evt)
-- | 'Data.UUID.V1.nextUUID' is sometimes unsuccessful, so we try a few times.
mkNotificationId :: (MonadIO m, MonadThrow m) => m NotificationId
diff --git a/services/galley/src/Galley/API/Update.hs b/services/galley/src/Galley/API/Update.hs
index 43190d9d83..7708afa49e 100644
--- a/services/galley/src/Galley/API/Update.hs
+++ b/services/galley/src/Galley/API/Update.hs
@@ -92,8 +92,6 @@ import Galley.API.Mapping
import Galley.API.Message
import Galley.API.Util
import Galley.App
-import Galley.Cassandra.Services
-import qualified Galley.Data.Access as Data
import qualified Galley.Data.Conversation as Data
import Galley.Data.Services as Data
import Galley.Data.Types hiding (Conversation)
@@ -107,6 +105,7 @@ import qualified Galley.Effects.ExternalAccess as E
import qualified Galley.Effects.FederatorAccess as E
import qualified Galley.Effects.GundeckAccess as E
import qualified Galley.Effects.MemberStore as E
+import qualified Galley.Effects.ServiceStore as E
import qualified Galley.Effects.TeamStore as E
import Galley.Intra.Push
import Galley.Options
@@ -282,6 +281,7 @@ performAccessUpdateAction ::
FederatorAccess,
FireAndForget,
GundeckAccess,
+ LegalHoldStore,
MemberStore,
TeamStore
]
@@ -457,6 +457,7 @@ type UpdateConversationActions =
GundeckAccess,
CodeStore,
ConversationStore,
+ LegalHoldStore,
MemberStore,
TeamStore
]
@@ -1432,14 +1433,14 @@ isTyping zusr zcon cnv typingData = do
& pushRoute .~ RouteDirect
& pushTransient .~ True
-addServiceH :: JsonRequest Service -> Galley r Response
+addServiceH :: Member ServiceStore r => JsonRequest Service -> Galley r Response
addServiceH req = do
- insertService =<< fromJsonBody req
+ liftSem . E.createService =<< fromJsonBody req
return empty
-rmServiceH :: JsonRequest ServiceRef -> Galley r Response
+rmServiceH :: Member ServiceStore r => JsonRequest ServiceRef -> Galley r Response
rmServiceH req = do
- deleteService =<< fromJsonBody req
+ liftSem . E.deleteService =<< fromJsonBody req
return empty
addBotH ::
diff --git a/services/galley/src/Galley/API/Util.hs b/services/galley/src/Galley/API/Util.hs
index 17c8a47415..5f434712e3 100644
--- a/services/galley/src/Galley/API/Util.hs
+++ b/services/galley/src/Galley/API/Util.hs
@@ -42,7 +42,6 @@ import Data.Time
import Galley.API.Error
import Galley.App
import qualified Galley.Data.Conversation as Data
-import Galley.Data.LegalHold (isTeamLegalholdWhitelisted)
import Galley.Data.Services (BotMember, newBotMember)
import qualified Galley.Data.Types as DataTypes
import Galley.Effects
@@ -52,6 +51,7 @@ import Galley.Effects.ConversationStore
import Galley.Effects.ExternalAccess
import Galley.Effects.FederatorAccess
import Galley.Effects.GundeckAccess
+import Galley.Effects.LegalHoldStore
import Galley.Effects.MemberStore
import Galley.Effects.TeamStore
import Galley.Intra.Push
@@ -797,7 +797,10 @@ anyLegalholdActivated uids = do
teamsOfUsers <- liftSem $ getUsersTeams uidsPage
anyM (\uid -> userLHEnabled <$> getLHStatus (Map.lookup uid teamsOfUsers) uid) uidsPage
-allLegalholdConsentGiven :: Member TeamStore r => [UserId] -> Galley r Bool
+allLegalholdConsentGiven ::
+ Members '[LegalHoldStore, TeamStore] r =>
+ [UserId] ->
+ Galley r Bool
allLegalholdConsentGiven uids = do
view (options . optSettings . setFeatureFlags . flagLegalHold) >>= \case
FeatureLegalHoldDisabledPermanently -> pure False
@@ -811,7 +814,7 @@ allLegalholdConsentGiven uids = do
-- conversation with user under legalhold.
flip allM (chunksOf 32 uids) $ \uidsPage -> do
teamsPage <- liftSem $ nub . Map.elems <$> getUsersTeams uidsPage
- allM isTeamLegalholdWhitelisted teamsPage
+ allM (liftSem . isTeamLegalholdWhitelisted) teamsPage
-- | Add to every uid the legalhold status
getLHStatusForUsers ::
diff --git a/services/galley/src/Galley/App.hs b/services/galley/src/Galley/App.hs
index cc0dfcd04b..bdb74e4992 100644
--- a/services/galley/src/Galley/App.hs
+++ b/services/galley/src/Galley/App.hs
@@ -91,8 +91,13 @@ import Galley.Cassandra.Code
import Galley.Cassandra.Conversation
import Galley.Cassandra.Conversation.Members
import Galley.Cassandra.ConversationList
+import Galley.Cassandra.CustomBackend
+import Galley.Cassandra.LegalHold
+import Galley.Cassandra.SearchVisibility
import Galley.Cassandra.Services
import Galley.Cassandra.Team
+import Galley.Cassandra.TeamFeatures
+import Galley.Cassandra.TeamNotifications
import Galley.Effects
import Galley.Effects.FireAndForget (interpretFireAndForget)
import qualified Galley.Effects.FireAndForget as E
@@ -158,12 +163,6 @@ instance MonadReader Env (Galley r) where
ask = Galley $ P.ask @Env
local f m = Galley $ P.local f (unGalley m)
-instance MonadClient (Galley r) where
- liftClient m = Galley $ do
- cs <- P.ask @ClientState
- embed @IO $ runClient cs m
- localState f m = Galley $ P.local f (unGalley m)
-
instance HasFederatorConfig (Galley r) where
federatorEndpoint = view federator
federationDomain = view (options . optSettings . setFederationDomain)
@@ -333,8 +332,13 @@ interpretGalleyToGalley0 =
. interpretConversationListToCassandra
. withLH interpretTeamMemberStoreToCassandra
. withLH interpretTeamStoreToCassandra
+ . interpretTeamNotificationStoreToCassandra
+ . interpretTeamFeatureStoreToCassandra
. interpretServiceStoreToCassandra
+ . interpretSearchVisibilityStoreToCassandra
. interpretMemberStoreToCassandra
+ . withLH interpretLegalHoldStoreToCassandra
+ . interpretCustomBackendStoreToCassandra
. interpretConversationStoreToCassandra
. interpretCodeStoreToCassandra
. interpretClientStoreToCassandra
diff --git a/services/galley/src/Galley/Data/Access.hs b/services/galley/src/Galley/Cassandra/Access.hs
similarity index 67%
rename from services/galley/src/Galley/Data/Access.hs
rename to services/galley/src/Galley/Cassandra/Access.hs
index e2dfb0b7f5..a2c4fb176b 100644
--- a/services/galley/src/Galley/Data/Access.hs
+++ b/services/galley/src/Galley/Cassandra/Access.hs
@@ -15,11 +15,10 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.Access where
+module Galley.Cassandra.Access where
import Cassandra
-import qualified Data.Set as Set
-import Galley.Data.Conversation.Types
+import Galley.Data.Conversation
import Imports hiding (Set)
import Wire.API.Conversation hiding (Conversation)
@@ -34,27 +33,5 @@ defAccess One2OneConv (Just (Set [])) = [PrivateAccess]
defAccess RegularConv (Just (Set [])) = defRegularConvAccess
defAccess _ (Just (Set (x : xs))) = x : xs
-defRegularConvAccess :: [Access]
-defRegularConvAccess = [InviteAccess]
-
-maybeRole :: ConvType -> Maybe AccessRole -> AccessRole
-maybeRole SelfConv _ = privateRole
-maybeRole ConnectConv _ = privateRole
-maybeRole One2OneConv _ = privateRole
-maybeRole RegularConv Nothing = defRole
-maybeRole RegularConv (Just r) = r
-
-defRole :: AccessRole
-defRole = ActivatedAccessRole
-
-privateRole :: AccessRole
-privateRole = PrivateAccessRole
-
privateOnly :: Set Access
privateOnly = Set [PrivateAccess]
-
-convAccessData :: Conversation -> ConversationAccessData
-convAccessData conv =
- ConversationAccessData
- (Set.fromList (convAccess conv))
- (convAccessRole conv)
diff --git a/services/galley/src/Galley/Cassandra/Client.hs b/services/galley/src/Galley/Cassandra/Client.hs
index ecf66e2f0f..ee0f4e3bda 100644
--- a/services/galley/src/Galley/Cassandra/Client.hs
+++ b/services/galley/src/Galley/Cassandra/Client.hs
@@ -25,8 +25,8 @@ import Cassandra
import Control.Arrow
import Data.Id
import Data.List.Split (chunksOf)
+import qualified Galley.Cassandra.Queries as Cql
import Galley.Cassandra.Store
-import qualified Galley.Data.Queries as Cql
import Galley.Effects.ClientStore (ClientStore (..))
import Galley.Types.Clients (Clients)
import qualified Galley.Types.Clients as Clients
diff --git a/services/galley/src/Galley/Cassandra/Code.hs b/services/galley/src/Galley/Cassandra/Code.hs
index c41e3c09b4..ac9d03525a 100644
--- a/services/galley/src/Galley/Cassandra/Code.hs
+++ b/services/galley/src/Galley/Cassandra/Code.hs
@@ -22,8 +22,8 @@ where
import Brig.Types.Code
import Cassandra
+import qualified Galley.Cassandra.Queries as Cql
import Galley.Cassandra.Store
-import qualified Galley.Data.Queries as Cql
import Galley.Data.Types
import Galley.Effects.CodeStore (CodeStore (..))
import Imports
diff --git a/services/galley/src/Galley/Cassandra/Conversation.hs b/services/galley/src/Galley/Cassandra/Conversation.hs
index 2522fd3ef6..04822d1850 100644
--- a/services/galley/src/Galley/Cassandra/Conversation.hs
+++ b/services/galley/src/Galley/Cassandra/Conversation.hs
@@ -22,7 +22,8 @@ module Galley.Cassandra.Conversation
)
where
-import Cassandra
+import Cassandra hiding (Set)
+import qualified Cassandra as Cql
import Data.ByteString.Conversion
import Data.Id
import qualified Data.Map as Map
@@ -31,12 +32,12 @@ import Data.Qualified
import Data.Range
import qualified Data.UUID.Tagged as U
import Data.UUID.V4 (nextRandom)
+import Galley.Cassandra.Access
import Galley.Cassandra.Conversation.Members
+import qualified Galley.Cassandra.Queries as Cql
import Galley.Cassandra.Store
-import Galley.Data.Access
import Galley.Data.Conversation
import Galley.Data.Conversation.Types
-import qualified Galley.Data.Queries as Cql
import Galley.Effects.ConversationStore (ConversationStore (..))
import Galley.Types.Conversations.Members
import Galley.Types.UserList
@@ -55,11 +56,11 @@ createConversation (NewConversation ty usr acc arole name mtid mtimer recpt user
conv <- Id <$> liftIO nextRandom
retry x5 $ case mtid of
Nothing ->
- write Cql.insertConv (params LocalQuorum (conv, ty, usr, Set (toList acc), arole, fmap fromRange name, Nothing, mtimer, recpt))
+ write Cql.insertConv (params LocalQuorum (conv, ty, usr, Cql.Set (toList acc), arole, fmap fromRange name, Nothing, mtimer, recpt))
Just tid -> batch $ do
setType BatchLogged
setConsistency LocalQuorum
- addPrepQuery Cql.insertConv (conv, ty, usr, Set (toList acc), arole, fmap fromRange name, Just tid, mtimer, recpt)
+ addPrepQuery Cql.insertConv (conv, ty, usr, Cql.Set (toList acc), arole, fmap fromRange name, Just tid, mtimer, recpt)
addPrepQuery Cql.insertTeamConv (tid, conv, False)
let newUsers = fmap (,role) (fromConvSize users)
(lmems, rmems) <- addMembers conv (ulAddLocal (usr, roleNameWireAdmin) newUsers)
@@ -249,7 +250,7 @@ updateConvName cid name = retry x5 $ write Cql.updateConvName (params LocalQuoru
updateConvAccess :: ConvId -> ConversationAccessData -> Client ()
updateConvAccess cid (ConversationAccessData acc role) =
retry x5 $
- write Cql.updateConvAccess (params LocalQuorum (Set (toList acc), role, cid))
+ write Cql.updateConvAccess (params LocalQuorum (Cql.Set (toList acc), role, cid))
updateConvReceiptMode :: ConvId -> ReceiptMode -> Client ()
updateConvReceiptMode cid receiptMode = retry x5 $ write Cql.updateConvReceiptMode (params LocalQuorum (receiptMode, cid))
@@ -332,6 +333,17 @@ remoteConversationStatusOnDomain uid rconvs =
toMemberStatus (omus, omur, oar, oarr, hid, hidr)
)
+toConv ::
+ ConvId ->
+ [LocalMember] ->
+ [RemoteMember] ->
+ Maybe (ConvType, UserId, Maybe (Cql.Set Access), Maybe AccessRole, Maybe Text, Maybe TeamId, Maybe Bool, Maybe Milliseconds, Maybe ReceiptMode) ->
+ Maybe Conversation
+toConv cid mms remoteMems conv =
+ f mms <$> conv
+ where
+ f ms (cty, uid, acc, role, nme, ti, del, timer, rm) = Conversation cid cty uid nme (defAccess cty acc) (maybeRole cty role) ms remoteMems ti del timer rm
+
interpretConversationStoreToCassandra ::
Members '[Embed IO, P.Reader ClientState, TinyLog] r =>
Sem (ConversationStore ': r) a ->
diff --git a/services/galley/src/Galley/Cassandra/Conversation/Members.hs b/services/galley/src/Galley/Cassandra/Conversation/Members.hs
index 3a69e61899..039fc64342 100644
--- a/services/galley/src/Galley/Cassandra/Conversation/Members.hs
+++ b/services/galley/src/Galley/Cassandra/Conversation/Members.hs
@@ -34,10 +34,10 @@ import qualified Data.List.Extra as List
import qualified Data.Map as Map
import Data.Monoid
import Data.Qualified
+import Galley.Cassandra.Instances ()
+import qualified Galley.Cassandra.Queries as Cql
import Galley.Cassandra.Services
import Galley.Cassandra.Store
-import Galley.Data.Instances ()
-import qualified Galley.Data.Queries as Cql
import Galley.Effects.MemberStore
import Galley.Types.Conversations.Members
import Galley.Types.ToUserRole
diff --git a/services/galley/src/Galley/Cassandra/ConversationList.hs b/services/galley/src/Galley/Cassandra/ConversationList.hs
index 76d8139dab..324b7fad34 100644
--- a/services/galley/src/Galley/Cassandra/ConversationList.hs
+++ b/services/galley/src/Galley/Cassandra/ConversationList.hs
@@ -26,11 +26,11 @@ import Cassandra
import Data.Id
import Data.Qualified
import Data.Range
+import Galley.Cassandra.Instances ()
import Galley.Cassandra.Paging
+import qualified Galley.Cassandra.Queries as Cql
+import Galley.Cassandra.ResultSet
import Galley.Cassandra.Store
-import Galley.Data.Instances ()
-import qualified Galley.Data.Queries as Cql
-import Galley.Data.ResultSet
import Galley.Effects.ListItems
import Imports hiding (max)
import Polysemy
diff --git a/services/galley/src/Galley/Data/CustomBackend.hs b/services/galley/src/Galley/Cassandra/CustomBackend.hs
similarity index 69%
rename from services/galley/src/Galley/Data/CustomBackend.hs
rename to services/galley/src/Galley/Cassandra/CustomBackend.hs
index ec6955e16b..fe757271b8 100644
--- a/services/galley/src/Galley/Data/CustomBackend.hs
+++ b/services/galley/src/Galley/Cassandra/CustomBackend.hs
@@ -17,19 +17,27 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.CustomBackend
- ( getCustomBackend,
- setCustomBackend,
- deleteCustomBackend,
- )
-where
+module Galley.Cassandra.CustomBackend (interpretCustomBackendStoreToCassandra) where
import Cassandra
import Data.Domain (Domain)
-import Galley.Data.Instances ()
-import qualified Galley.Data.Queries as Cql
+import Galley.Cassandra.Instances ()
+import qualified Galley.Cassandra.Queries as Cql
+import Galley.Cassandra.Store
+import Galley.Effects.CustomBackendStore (CustomBackendStore (..))
import Galley.Types
import Imports
+import Polysemy
+import qualified Polysemy.Reader as P
+
+interpretCustomBackendStoreToCassandra ::
+ Members '[Embed IO, P.Reader ClientState] r =>
+ Sem (CustomBackendStore ': r) a ->
+ Sem r a
+interpretCustomBackendStoreToCassandra = interpret $ \case
+ GetCustomBackend dom -> embedClient $ getCustomBackend dom
+ SetCustomBackend dom b -> embedClient $ setCustomBackend dom b
+ DeleteCustomBackend dom -> embedClient $ deleteCustomBackend dom
getCustomBackend :: MonadClient m => Domain -> m (Maybe CustomBackend)
getCustomBackend domain =
diff --git a/services/galley/src/Galley/Data/Instances.hs b/services/galley/src/Galley/Cassandra/Instances.hs
similarity index 99%
rename from services/galley/src/Galley/Data/Instances.hs
rename to services/galley/src/Galley/Cassandra/Instances.hs
index b1d259548e..198aa2675c 100644
--- a/services/galley/src/Galley/Data/Instances.hs
+++ b/services/galley/src/Galley/Cassandra/Instances.hs
@@ -18,7 +18,7 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.Instances
+module Galley.Cassandra.Instances
(
)
where
diff --git a/services/galley/src/Galley/Cassandra/LegalHold.hs b/services/galley/src/Galley/Cassandra/LegalHold.hs
index 12f687abd8..87345d5c79 100644
--- a/services/galley/src/Galley/Cassandra/LegalHold.hs
+++ b/services/galley/src/Galley/Cassandra/LegalHold.hs
@@ -15,13 +15,99 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Cassandra.LegalHold (isTeamLegalholdWhitelisted) where
+module Galley.Cassandra.LegalHold
+ ( interpretLegalHoldStoreToCassandra,
+ isTeamLegalholdWhitelisted,
+ -- * Used by tests
+ selectPendingPrekeys,
+ )
+where
+
+import Brig.Types.Client.Prekey
+import Brig.Types.Instances ()
+import Brig.Types.Team.LegalHold
import Cassandra
+import Control.Lens (unsnoc)
import Data.Id
-import Galley.Data.Queries as Q
+import Data.LegalHold
+import Galley.Cassandra.Instances ()
+import qualified Galley.Cassandra.Queries as Q
+import Galley.Cassandra.Store
+import Galley.Effects.LegalHoldStore (LegalHoldStore (..))
import Galley.Types.Teams
import Imports
+import Polysemy
+import qualified Polysemy.Reader as P
+
+interpretLegalHoldStoreToCassandra ::
+ Members '[Embed IO, P.Reader ClientState] r =>
+ FeatureLegalHold ->
+ Sem (LegalHoldStore ': r) a ->
+ Sem r a
+interpretLegalHoldStoreToCassandra lh = interpret $ \case
+ CreateSettings s -> embedClient $ createSettings s
+ GetSettings tid -> embedClient $ getSettings tid
+ RemoveSettings tid -> embedClient $ removeSettings tid
+ InsertPendingPrekeys uid pkeys -> embedClient $ insertPendingPrekeys uid pkeys
+ SelectPendingPrekeys uid -> embedClient $ selectPendingPrekeys uid
+ DropPendingPrekeys uid -> embedClient $ dropPendingPrekeys uid
+ SetUserLegalHoldStatus tid uid st -> embedClient $ setUserLegalHoldStatus tid uid st
+ SetTeamLegalholdWhitelisted tid -> embedClient $ setTeamLegalholdWhitelisted tid
+ UnsetTeamLegalholdWhitelisted tid -> embedClient $ unsetTeamLegalholdWhitelisted tid
+ IsTeamLegalholdWhitelisted tid -> embedClient $ isTeamLegalholdWhitelisted lh tid
+
+-- | Returns 'False' if legal hold is not enabled for this team
+-- The Caller is responsible for checking whether legal hold is enabled for this team
+createSettings :: MonadClient m => LegalHoldService -> m ()
+createSettings (LegalHoldService tid url fpr tok key) = do
+ retry x1 $ write Q.insertLegalHoldSettings (params LocalQuorum (url, fpr, tok, key, tid))
+
+-- | Returns 'Nothing' if no settings are saved
+-- The Caller is responsible for checking whether legal hold is enabled for this team
+getSettings :: MonadClient m => TeamId -> m (Maybe LegalHoldService)
+getSettings tid =
+ fmap toLegalHoldService <$> do
+ retry x1 $ query1 Q.selectLegalHoldSettings (params LocalQuorum (Identity tid))
+ where
+ toLegalHoldService (httpsUrl, fingerprint, tok, key) = LegalHoldService tid httpsUrl fingerprint tok key
+
+removeSettings :: MonadClient m => TeamId -> m ()
+removeSettings tid = retry x5 (write Q.removeLegalHoldSettings (params LocalQuorum (Identity tid)))
+
+insertPendingPrekeys :: MonadClient m => UserId -> [Prekey] -> m ()
+insertPendingPrekeys uid keys = retry x5 . batch $
+ forM_ keys $
+ \key ->
+ addPrepQuery Q.insertPendingPrekeys (toTuple key)
+ where
+ toTuple (Prekey keyId key) = (uid, keyId, key)
+
+selectPendingPrekeys :: MonadClient m => UserId -> m (Maybe ([Prekey], LastPrekey))
+selectPendingPrekeys uid =
+ pickLastKey . fmap fromTuple
+ <$> retry x1 (query Q.selectPendingPrekeys (params LocalQuorum (Identity uid)))
+ where
+ fromTuple (keyId, key) = Prekey keyId key
+ pickLastKey allPrekeys =
+ case unsnoc allPrekeys of
+ Nothing -> Nothing
+ Just (keys, lst) -> pure (keys, lastPrekey . prekeyKey $ lst)
+
+dropPendingPrekeys :: MonadClient m => UserId -> m ()
+dropPendingPrekeys uid = retry x5 (write Q.dropPendingPrekeys (params LocalQuorum (Identity uid)))
+
+setUserLegalHoldStatus :: MonadClient m => TeamId -> UserId -> UserLegalHoldStatus -> m ()
+setUserLegalHoldStatus tid uid status =
+ retry x5 (write Q.updateUserLegalHoldStatus (params LocalQuorum (status, tid, uid)))
+
+setTeamLegalholdWhitelisted :: MonadClient m => TeamId -> m ()
+setTeamLegalholdWhitelisted tid =
+ retry x5 (write Q.insertLegalHoldWhitelistedTeam (params LocalQuorum (Identity tid)))
+
+unsetTeamLegalholdWhitelisted :: MonadClient m => TeamId -> m ()
+unsetTeamLegalholdWhitelisted tid =
+ retry x5 (write Q.removeLegalHoldWhitelistedTeam (params LocalQuorum (Identity tid)))
isTeamLegalholdWhitelisted :: FeatureLegalHold -> TeamId -> Client Bool
isTeamLegalholdWhitelisted FeatureLegalHoldDisabledPermanently _ = pure False
diff --git a/services/galley/src/Galley/Cassandra/Paging.hs b/services/galley/src/Galley/Cassandra/Paging.hs
index d2ea8e4cd2..f4dd6a07c7 100644
--- a/services/galley/src/Galley/Cassandra/Paging.hs
+++ b/services/galley/src/Galley/Cassandra/Paging.hs
@@ -23,6 +23,12 @@ module Galley.Cassandra.Paging
InternalPagingState (..),
mkInternalPage,
ipNext,
+
+ -- * Re-exports
+ ResultSet,
+ resultSetResult,
+ resultSetType,
+ ResultSetType (..),
)
where
@@ -30,7 +36,7 @@ import Cassandra
import Data.Id
import Data.Qualified
import Data.Range
-import Galley.Data.ResultSet
+import Galley.Cassandra.ResultSet
import qualified Galley.Effects.Paging as E
import Imports
import Wire.API.Team.Member (HardTruncationLimit, TeamMember)
diff --git a/services/galley/src/Galley/Data/Queries.hs b/services/galley/src/Galley/Cassandra/Queries.hs
similarity index 99%
rename from services/galley/src/Galley/Data/Queries.hs
rename to services/galley/src/Galley/Cassandra/Queries.hs
index fc919ac312..641fdcbe57 100644
--- a/services/galley/src/Galley/Data/Queries.hs
+++ b/services/galley/src/Galley/Cassandra/Queries.hs
@@ -15,7 +15,7 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.Queries where
+module Galley.Cassandra.Queries where
import Brig.Types.Client.Prekey
import Brig.Types.Code
diff --git a/services/galley/src/Galley/Data/ResultSet.hs b/services/galley/src/Galley/Cassandra/ResultSet.hs
similarity index 98%
rename from services/galley/src/Galley/Data/ResultSet.hs
rename to services/galley/src/Galley/Cassandra/ResultSet.hs
index 78db286a0e..441a5baa40 100644
--- a/services/galley/src/Galley/Data/ResultSet.hs
+++ b/services/galley/src/Galley/Cassandra/ResultSet.hs
@@ -15,7 +15,7 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.ResultSet where
+module Galley.Cassandra.ResultSet where
import Cassandra
import Imports
diff --git a/services/galley/src/Galley/Data/SearchVisibility.hs b/services/galley/src/Galley/Cassandra/SearchVisibility.hs
similarity index 71%
rename from services/galley/src/Galley/Data/SearchVisibility.hs
rename to services/galley/src/Galley/Cassandra/SearchVisibility.hs
index f291fae8d1..cd3905ad4c 100644
--- a/services/galley/src/Galley/Data/SearchVisibility.hs
+++ b/services/galley/src/Galley/Cassandra/SearchVisibility.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ViewPatterns #-}
-
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2020 Wire Swiss GmbH
@@ -17,19 +15,27 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.SearchVisibility
- ( setSearchVisibility,
- getSearchVisibility,
- resetSearchVisibility,
- )
-where
+module Galley.Cassandra.SearchVisibility (interpretSearchVisibilityStoreToCassandra) where
import Cassandra
import Data.Id
-import Galley.Data.Instances ()
-import Galley.Data.Queries
+import Galley.Cassandra.Instances ()
+import Galley.Cassandra.Queries
+import Galley.Cassandra.Store
+import Galley.Effects.SearchVisibilityStore (SearchVisibilityStore (..))
import Galley.Types.Teams.SearchVisibility
import Imports
+import Polysemy
+import qualified Polysemy.Reader as P
+
+interpretSearchVisibilityStoreToCassandra ::
+ Members '[Embed IO, P.Reader ClientState] r =>
+ Sem (SearchVisibilityStore ': r) a ->
+ Sem r a
+interpretSearchVisibilityStoreToCassandra = interpret $ \case
+ GetSearchVisibility tid -> embedClient $ getSearchVisibility tid
+ SetSearchVisibility tid value -> embedClient $ setSearchVisibility tid value
+ ResetSearchVisibility tid -> embedClient $ resetSearchVisibility tid
-- | Return whether a given team is allowed to enable/disable sso
getSearchVisibility :: MonadClient m => TeamId -> m TeamSearchVisibility
diff --git a/services/galley/src/Galley/Cassandra/Services.hs b/services/galley/src/Galley/Cassandra/Services.hs
index b6e7f7403f..724c5dab5f 100644
--- a/services/galley/src/Galley/Cassandra/Services.hs
+++ b/services/galley/src/Galley/Cassandra/Services.hs
@@ -20,8 +20,8 @@ module Galley.Cassandra.Services where
import Cassandra
import Control.Lens
import Data.Id
+import Galley.Cassandra.Queries
import Galley.Cassandra.Store
-import Galley.Data.Queries
import Galley.Data.Services
import Galley.Effects.ServiceStore (ServiceStore (..))
import Galley.Types hiding (Conversation)
diff --git a/services/galley/src/Galley/Cassandra/Team.hs b/services/galley/src/Galley/Cassandra/Team.hs
index 709b236d1a..9e5ece8d00 100644
--- a/services/galley/src/Galley/Cassandra/Team.hs
+++ b/services/galley/src/Galley/Cassandra/Team.hs
@@ -39,10 +39,9 @@ import Data.UUID.V4 (nextRandom)
import qualified Galley.Cassandra.Conversation as C
import Galley.Cassandra.LegalHold (isTeamLegalholdWhitelisted)
import Galley.Cassandra.Paging
+import qualified Galley.Cassandra.Queries as Cql
+import Galley.Cassandra.ResultSet
import Galley.Cassandra.Store
-import Galley.Data.Instances ()
-import qualified Galley.Data.Queries as Cql
-import Galley.Data.ResultSet
import Galley.Effects.ListItems
import Galley.Effects.TeamMemberStore
import Galley.Effects.TeamStore (TeamStore (..))
diff --git a/services/galley/src/Galley/Cassandra/TeamFeatures.hs b/services/galley/src/Galley/Cassandra/TeamFeatures.hs
new file mode 100644
index 0000000000..7ef181c87a
--- /dev/null
+++ b/services/galley/src/Galley/Cassandra/TeamFeatures.hs
@@ -0,0 +1,153 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2020 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+module Galley.Cassandra.TeamFeatures (interpretTeamFeatureStoreToCassandra) where
+
+import Cassandra
+import Data.Id
+import Data.Proxy
+import Galley.Cassandra.Instances ()
+import Galley.Cassandra.Store
+import Galley.Data.TeamFeatures
+import Galley.Effects.TeamFeatureStore (TeamFeatureStore (..))
+import Imports
+import Polysemy
+import qualified Polysemy.Reader as P
+import Wire.API.Team.Feature
+
+getFeatureStatusNoConfig ::
+ forall (a :: TeamFeatureName) m.
+ ( MonadClient m,
+ FeatureHasNoConfig a,
+ HasStatusCol a
+ ) =>
+ Proxy a ->
+ TeamId ->
+ m (Maybe (TeamFeatureStatus a))
+getFeatureStatusNoConfig _ tid = do
+ let q = query1 select (params LocalQuorum (Identity tid))
+ mStatusValue <- (>>= runIdentity) <$> retry x1 q
+ pure $ TeamFeatureStatusNoConfig <$> mStatusValue
+ where
+ select :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatusValue))
+ select = fromString $ "select " <> statusCol @a <> " from team_features where team_id = ?"
+
+setFeatureStatusNoConfig ::
+ forall (a :: TeamFeatureName) m.
+ ( MonadClient m,
+ FeatureHasNoConfig a,
+ HasStatusCol a
+ ) =>
+ Proxy a ->
+ TeamId ->
+ TeamFeatureStatus a ->
+ m (TeamFeatureStatus a)
+setFeatureStatusNoConfig _ tid status = do
+ let flag = tfwoStatus status
+ retry x5 $ write insert (params LocalQuorum (tid, flag))
+ pure status
+ where
+ insert :: PrepQuery W (TeamId, TeamFeatureStatusValue) ()
+ insert = fromString $ "insert into team_features (team_id, " <> statusCol @a <> ") values (?, ?)"
+
+getApplockFeatureStatus ::
+ forall m.
+ (MonadClient m) =>
+ TeamId ->
+ m (Maybe (TeamFeatureStatus 'TeamFeatureAppLock))
+getApplockFeatureStatus tid = do
+ let q = query1 select (params LocalQuorum (Identity tid))
+ mTuple <- retry x1 q
+ pure $
+ mTuple >>= \(mbStatusValue, mbEnforce, mbTimeout) ->
+ TeamFeatureStatusWithConfig <$> mbStatusValue <*> (TeamFeatureAppLockConfig <$> mbEnforce <*> mbTimeout)
+ where
+ select :: PrepQuery R (Identity TeamId) (Maybe TeamFeatureStatusValue, Maybe EnforceAppLock, Maybe Int32)
+ select =
+ fromString $
+ "select " <> statusCol @'TeamFeatureAppLock <> ", app_lock_enforce, app_lock_inactivity_timeout_secs "
+ <> "from team_features where team_id = ?"
+
+setApplockFeatureStatus ::
+ (MonadClient m) =>
+ TeamId ->
+ TeamFeatureStatus 'TeamFeatureAppLock ->
+ m (TeamFeatureStatus 'TeamFeatureAppLock)
+setApplockFeatureStatus tid status = do
+ let statusValue = tfwcStatus status
+ enforce = applockEnforceAppLock . tfwcConfig $ status
+ timeout = applockInactivityTimeoutSecs . tfwcConfig $ status
+ retry x5 $ write insert (params LocalQuorum (tid, statusValue, enforce, timeout))
+ pure status
+ where
+ insert :: PrepQuery W (TeamId, TeamFeatureStatusValue, EnforceAppLock, Int32) ()
+ insert =
+ fromString $
+ "insert into team_features (team_id, "
+ <> statusCol @'TeamFeatureAppLock
+ <> ", app_lock_enforce, app_lock_inactivity_timeout_secs) values (?, ?, ?, ?)"
+
+getSelfDeletingMessagesStatus ::
+ forall m.
+ (MonadClient m) =>
+ TeamId ->
+ m (Maybe (TeamFeatureStatus 'TeamFeatureSelfDeletingMessages))
+getSelfDeletingMessagesStatus tid = do
+ let q = query1 select (params LocalQuorum (Identity tid))
+ mTuple <- retry x1 q
+ pure $
+ mTuple >>= \(mbStatusValue, mbTimeout) ->
+ TeamFeatureStatusWithConfig <$> mbStatusValue <*> (TeamFeatureSelfDeletingMessagesConfig <$> mbTimeout)
+ where
+ select :: PrepQuery R (Identity TeamId) (Maybe TeamFeatureStatusValue, Maybe Int32)
+ select =
+ fromString $
+ "select "
+ <> statusCol @'TeamFeatureSelfDeletingMessages
+ <> ", self_deleting_messages_ttl "
+ <> "from team_features where team_id = ?"
+
+setSelfDeletingMessagesStatus ::
+ (MonadClient m) =>
+ TeamId ->
+ TeamFeatureStatus 'TeamFeatureSelfDeletingMessages ->
+ m (TeamFeatureStatus 'TeamFeatureSelfDeletingMessages)
+setSelfDeletingMessagesStatus tid status = do
+ let statusValue = tfwcStatus status
+ timeout = sdmEnforcedTimeoutSeconds . tfwcConfig $ status
+ retry x5 $ write insert (params LocalQuorum (tid, statusValue, timeout))
+ pure status
+ where
+ insert :: PrepQuery W (TeamId, TeamFeatureStatusValue, Int32) ()
+ insert =
+ fromString $
+ "insert into team_features (team_id, "
+ <> statusCol @'TeamFeatureSelfDeletingMessages
+ <> ", self_deleting_messages_ttl) "
+ <> "values (?, ?, ?)"
+
+interpretTeamFeatureStoreToCassandra ::
+ Members '[Embed IO, P.Reader ClientState] r =>
+ Sem (TeamFeatureStore ': r) a ->
+ Sem r a
+interpretTeamFeatureStoreToCassandra = interpret $ \case
+ GetFeatureStatusNoConfig' p tid -> embedClient $ getFeatureStatusNoConfig p tid
+ SetFeatureStatusNoConfig' p tid value -> embedClient $ setFeatureStatusNoConfig p tid value
+ GetApplockFeatureStatus tid -> embedClient $ getApplockFeatureStatus tid
+ SetApplockFeatureStatus tid value -> embedClient $ setApplockFeatureStatus tid value
+ GetSelfDeletingMessagesStatus tid -> embedClient $ getSelfDeletingMessagesStatus tid
+ SetSelfDeletingMessagesStatus tid value -> embedClient $ setSelfDeletingMessagesStatus tid value
diff --git a/services/galley/src/Galley/Cassandra/TeamNotifications.hs b/services/galley/src/Galley/Cassandra/TeamNotifications.hs
new file mode 100644
index 0000000000..2a12e34737
--- /dev/null
+++ b/services/galley/src/Galley/Cassandra/TeamNotifications.hs
@@ -0,0 +1,139 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2020 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+-- | See also: "Galley.API.TeamNotifications".
+--
+-- This module is a clone of "Gundeck.Notification.Data".
+--
+-- FUTUREWORK: this is a work-around because it only solves *some* problems with team events.
+-- We should really use a scalable message queue instead.
+module Galley.Cassandra.TeamNotifications
+ ( interpretTeamNotificationStoreToCassandra,
+ )
+where
+
+import Cassandra
+import qualified Data.Aeson as JSON
+import Data.Id
+import Data.List1 (List1)
+import Data.Range (Range, fromRange)
+import Data.Sequence (Seq, ViewL (..), ViewR (..), (<|), (><))
+import qualified Data.Sequence as Seq
+import Galley.Cassandra.Store
+import Galley.Data.TeamNotifications
+import Galley.Effects.TeamNotificationStore
+import Gundeck.Types.Notification
+import Imports
+import Polysemy
+import qualified Polysemy.Reader as P
+
+interpretTeamNotificationStoreToCassandra ::
+ Members '[Embed IO, P.Reader ClientState] r =>
+ Sem (TeamNotificationStore ': r) a ->
+ Sem r a
+interpretTeamNotificationStoreToCassandra = interpret $ \case
+ CreateTeamNotification tid nid objs -> embedClient $ add tid nid objs
+ GetTeamNotifications tid mnid lim -> embedClient $ fetch tid mnid lim
+
+-- FUTUREWORK: the magic 32 should be made configurable, so it can be tuned
+add ::
+ TeamId ->
+ NotificationId ->
+ List1 JSON.Object ->
+ Client ()
+add tid nid (Blob . JSON.encode -> payload) =
+ write cqlInsert (params LocalQuorum (tid, nid, payload, notificationTTLSeconds)) & retry x5
+ where
+ cqlInsert :: PrepQuery W (TeamId, NotificationId, Blob, Int32) ()
+ cqlInsert =
+ "INSERT INTO team_notifications \
+ \(team, id, payload) VALUES \
+ \(?, ?, ?) \
+ \USING TTL ?"
+
+notificationTTLSeconds :: Int32
+notificationTTLSeconds = 24192200
+
+fetch :: TeamId -> Maybe NotificationId -> Range 1 10000 Int32 -> Client ResultPage
+fetch tid since (fromRange -> size) = do
+ -- We always need to look for one more than requested in order to correctly
+ -- report whether there are more results.
+ let size' = bool (+ 1) (+ 2) (isJust since) size
+ page1 <- case TimeUuid . toUUID <$> since of
+ Nothing -> paginate cqlStart (paramsP LocalQuorum (Identity tid) size') & retry x1
+ Just s -> paginate cqlSince (paramsP LocalQuorum (tid, s) size') & retry x1
+ -- Collect results, requesting more pages until we run out of data
+ -- or have found size + 1 notifications (not including the 'since').
+ let isize = fromIntegral size' :: Int
+ (ns, more) <- collect Seq.empty isize page1
+ -- Drop the extra element from the end as well. Keep the inclusive start
+ -- value in the response (if a 'since' was given and found).
+ -- This can probably simplified a lot further, but we need to understand
+ -- 'Seq' in order to do that. If you find a bug, this may be a good
+ -- place to start looking.
+ return $! case Seq.viewl (trim (isize - 1) ns) of
+ EmptyL -> ResultPage Seq.empty False
+ (x :< xs) -> ResultPage (x <| xs) more
+ where
+ collect ::
+ Seq QueuedNotification ->
+ Int ->
+ Page (TimeUuid, Blob) ->
+ Client (Seq QueuedNotification, Bool)
+ collect acc num page =
+ let ns = splitAt num $ foldr toNotif [] (result page)
+ nseq = Seq.fromList (fst ns)
+ more = hasMore page
+ num' = num - Seq.length nseq
+ acc' = acc >< nseq
+ in if not more || num' == 0
+ then return (acc', more || not (null (snd ns)))
+ else liftClient (nextPage page) >>= collect acc' num'
+ trim :: Int -> Seq a -> Seq a
+ trim l ns
+ | Seq.length ns <= l = ns
+ | otherwise = case Seq.viewr ns of
+ EmptyR -> ns
+ xs :> _ -> xs
+ cqlStart :: PrepQuery R (Identity TeamId) (TimeUuid, Blob)
+ cqlStart =
+ "SELECT id, payload \
+ \FROM team_notifications \
+ \WHERE team = ? \
+ \ORDER BY id ASC"
+ cqlSince :: PrepQuery R (TeamId, TimeUuid) (TimeUuid, Blob)
+ cqlSince =
+ "SELECT id, payload \
+ \FROM team_notifications \
+ \WHERE team = ? AND id >= ? \
+ \ORDER BY id ASC"
+
+-------------------------------------------------------------------------------
+-- Conversions
+
+toNotif :: (TimeUuid, Blob) -> [QueuedNotification] -> [QueuedNotification]
+toNotif (i, b) ns =
+ maybe
+ ns
+ (\p1 -> queuedNotification notifId p1 : ns)
+ ( JSON.decode' (fromBlob b)
+ -- FUTUREWORK: this is from the database, so it's slightly more ok to ignore parse
+ -- errors than if it's data provided by a client. it would still be better to have an
+ -- error entry in the log file and crash, rather than ignore the error and continue.
+ )
+ where
+ notifId = Id (fromTimeUuid i)
diff --git a/services/galley/src/Galley/Data/Conversation.hs b/services/galley/src/Galley/Data/Conversation.hs
index 725161d7f0..b1e149b651 100644
--- a/services/galley/src/Galley/Data/Conversation.hs
+++ b/services/galley/src/Galley/Data/Conversation.hs
@@ -26,20 +26,21 @@ module Galley.Data.Conversation
isTeamConv,
isConvDeleted,
selfConv,
- toConv,
localOne2OneConvId,
convMetadata,
+ convAccessData,
+ defRole,
+ maybeRole,
+ privateRole,
+ defRegularConvAccess,
)
where
-import Cassandra
import Data.Id
-import Data.Misc
+import qualified Data.Set as Set
import qualified Data.UUID.Tagged as U
-import Galley.Data.Access
+import Galley.Cassandra.Instances ()
import Galley.Data.Conversation.Types
-import Galley.Data.Instances ()
-import Galley.Types.Conversations.Members
import Imports hiding (Set)
import Wire.API.Conversation hiding (Conversation)
@@ -58,17 +59,6 @@ isConvDeleted = fromMaybe False . convDeleted
selfConv :: UserId -> ConvId
selfConv uid = Id (toUUID uid)
-toConv ::
- ConvId ->
- [LocalMember] ->
- [RemoteMember] ->
- Maybe (ConvType, UserId, Maybe (Set Access), Maybe AccessRole, Maybe Text, Maybe TeamId, Maybe Bool, Maybe Milliseconds, Maybe ReceiptMode) ->
- Maybe Conversation
-toConv cid mms remoteMems conv =
- f mms <$> conv
- where
- f ms (cty, uid, acc, role, nme, ti, del, timer, rm) = Conversation cid cty uid nme (defAccess cty acc) (maybeRole cty role) ms remoteMems ti del timer rm
-
-- | We deduce the conversation ID by adding the 4 components of the V4 UUID
-- together pairwise, and then setting the version bits (v4) and variant bits
-- (variant 2). This means that we always know what the UUID is for a
@@ -87,3 +77,25 @@ convMetadata c =
(convTeam c)
(convMessageTimer c)
(convReceiptMode c)
+
+convAccessData :: Conversation -> ConversationAccessData
+convAccessData conv =
+ ConversationAccessData
+ (Set.fromList (convAccess conv))
+ (convAccessRole conv)
+
+defRole :: AccessRole
+defRole = ActivatedAccessRole
+
+maybeRole :: ConvType -> Maybe AccessRole -> AccessRole
+maybeRole SelfConv _ = privateRole
+maybeRole ConnectConv _ = privateRole
+maybeRole One2OneConv _ = privateRole
+maybeRole RegularConv Nothing = defRole
+maybeRole RegularConv (Just r) = r
+
+privateRole :: AccessRole
+privateRole = PrivateAccessRole
+
+defRegularConvAccess :: [Access]
+defRegularConvAccess = [InviteAccess]
diff --git a/services/galley/src/Galley/Data/LegalHold.hs b/services/galley/src/Galley/Data/LegalHold.hs
deleted file mode 100644
index 716e0917df..0000000000
--- a/services/galley/src/Galley/Data/LegalHold.hs
+++ /dev/null
@@ -1,102 +0,0 @@
--- This file is part of the Wire Server implementation.
---
--- Copyright (C) 2020 Wire Swiss GmbH
---
--- This program is free software: you can redistribute it and/or modify it under
--- the terms of the GNU Affero General Public License as published by the Free
--- Software Foundation, either version 3 of the License, or (at your option) any
--- later version.
---
--- This program is distributed in the hope that it will be useful, but WITHOUT
--- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
--- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
--- details.
---
--- You should have received a copy of the GNU Affero General Public License along
--- with this program. If not, see .
-
-module Galley.Data.LegalHold
- ( createSettings,
- getSettings,
- removeSettings,
- Galley.Data.LegalHold.insertPendingPrekeys,
- Galley.Data.LegalHold.selectPendingPrekeys,
- Galley.Data.LegalHold.dropPendingPrekeys,
- setUserLegalHoldStatus,
- setTeamLegalholdWhitelisted,
- isTeamLegalholdWhitelisted,
- unsetTeamLegalholdWhitelisted,
- )
-where
-
-import Brig.Types.Client.Prekey
-import Brig.Types.Instances ()
-import Brig.Types.Team.LegalHold
-import Cassandra
-import Control.Lens (unsnoc, view)
-import Data.Id
-import Data.LegalHold
-import qualified Galley.Cassandra.LegalHold as C
-import Galley.Data.Instances ()
-import Galley.Data.Queries as Q
-import Galley.Env
-import qualified Galley.Options as Opts
-import Galley.Types.Teams (flagLegalHold)
-import Imports
-
--- | Returns 'False' if legal hold is not enabled for this team
--- The Caller is responsible for checking whether legal hold is enabled for this team
-createSettings :: MonadClient m => LegalHoldService -> m ()
-createSettings (LegalHoldService tid url fpr tok key) = do
- retry x1 $ write insertLegalHoldSettings (params LocalQuorum (url, fpr, tok, key, tid))
-
--- | Returns 'Nothing' if no settings are saved
--- The Caller is responsible for checking whether legal hold is enabled for this team
-getSettings :: MonadClient m => TeamId -> m (Maybe LegalHoldService)
-getSettings tid =
- fmap toLegalHoldService <$> do
- retry x1 $ query1 selectLegalHoldSettings (params LocalQuorum (Identity tid))
- where
- toLegalHoldService (httpsUrl, fingerprint, tok, key) = LegalHoldService tid httpsUrl fingerprint tok key
-
-removeSettings :: MonadClient m => TeamId -> m ()
-removeSettings tid = retry x5 (write removeLegalHoldSettings (params LocalQuorum (Identity tid)))
-
-insertPendingPrekeys :: MonadClient m => UserId -> [Prekey] -> m ()
-insertPendingPrekeys uid keys = retry x5 . batch $
- forM_ keys $
- \key ->
- addPrepQuery Q.insertPendingPrekeys (toTuple key)
- where
- toTuple (Prekey keyId key) = (uid, keyId, key)
-
-selectPendingPrekeys :: MonadClient m => UserId -> m (Maybe ([Prekey], LastPrekey))
-selectPendingPrekeys uid =
- pickLastKey . fmap fromTuple
- <$> retry x1 (query Q.selectPendingPrekeys (params LocalQuorum (Identity uid)))
- where
- fromTuple (keyId, key) = Prekey keyId key
- pickLastKey allPrekeys =
- case unsnoc allPrekeys of
- Nothing -> Nothing
- Just (keys, lst) -> pure (keys, lastPrekey . prekeyKey $ lst)
-
-dropPendingPrekeys :: MonadClient m => UserId -> m ()
-dropPendingPrekeys uid = retry x5 (write Q.dropPendingPrekeys (params LocalQuorum (Identity uid)))
-
-setUserLegalHoldStatus :: MonadClient m => TeamId -> UserId -> UserLegalHoldStatus -> m ()
-setUserLegalHoldStatus tid uid status =
- retry x5 (write Q.updateUserLegalHoldStatus (params LocalQuorum (status, tid, uid)))
-
-setTeamLegalholdWhitelisted :: MonadClient m => TeamId -> m ()
-setTeamLegalholdWhitelisted tid =
- retry x5 (write Q.insertLegalHoldWhitelistedTeam (params LocalQuorum (Identity tid)))
-
-unsetTeamLegalholdWhitelisted :: MonadClient m => TeamId -> m ()
-unsetTeamLegalholdWhitelisted tid =
- retry x5 (write Q.removeLegalHoldWhitelistedTeam (params LocalQuorum (Identity tid)))
-
-isTeamLegalholdWhitelisted :: (MonadReader Env m, MonadClient m) => TeamId -> m Bool
-isTeamLegalholdWhitelisted tid = do
- lhFlag <- view (options . Opts.optSettings . Opts.setFeatureFlags . flagLegalHold)
- liftClient $ C.isTeamLegalholdWhitelisted lhFlag tid
diff --git a/services/galley/src/Galley/Data/TeamFeatures.hs b/services/galley/src/Galley/Data/TeamFeatures.hs
index a9437a3470..e7ab337d0f 100644
--- a/services/galley/src/Galley/Data/TeamFeatures.hs
+++ b/services/galley/src/Galley/Data/TeamFeatures.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ViewPatterns #-}
-
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2020 Wire Swiss GmbH
@@ -17,29 +15,10 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Galley.Data.TeamFeatures
- ( getFeatureStatusNoConfig,
- setFeatureStatusNoConfig,
- getApplockFeatureStatus,
- setApplockFeatureStatus,
- getSelfDeletingMessagesStatus,
- setSelfDeletingMessagesStatus,
- HasStatusCol (..),
- )
-where
+module Galley.Data.TeamFeatures (HasStatusCol (..)) where
-import Cassandra
-import Data.Id
-import Galley.Data.Instances ()
import Imports
import Wire.API.Team.Feature
- ( TeamFeatureName (..),
- TeamFeatureStatus,
- TeamFeatureStatusNoConfig (..),
- TeamFeatureStatusValue (..),
- TeamFeatureStatusWithConfig (..),
- )
-import qualified Wire.API.Team.Feature as Public
-- | Because not all so called team features are actually team-level features,
-- not all of them have a corresponding column in the database. Therefore,
@@ -69,112 +48,3 @@ instance HasStatusCol 'TeamFeatureFileSharing where statusCol = "file_sharing"
instance HasStatusCol 'TeamFeatureConferenceCalling where statusCol = "conference_calling"
instance HasStatusCol 'TeamFeatureSelfDeletingMessages where statusCol = "self_deleting_messages_status"
-
-getFeatureStatusNoConfig ::
- forall (a :: Public.TeamFeatureName) m.
- ( MonadClient m,
- Public.FeatureHasNoConfig a,
- HasStatusCol a
- ) =>
- TeamId ->
- m (Maybe (TeamFeatureStatus a))
-getFeatureStatusNoConfig tid = do
- let q = query1 select (params LocalQuorum (Identity tid))
- mStatusValue <- (>>= runIdentity) <$> retry x1 q
- pure $ TeamFeatureStatusNoConfig <$> mStatusValue
- where
- select :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatusValue))
- select = fromString $ "select " <> statusCol @a <> " from team_features where team_id = ?"
-
-setFeatureStatusNoConfig ::
- forall (a :: Public.TeamFeatureName) m.
- ( MonadClient m,
- Public.FeatureHasNoConfig a,
- HasStatusCol a
- ) =>
- TeamId ->
- TeamFeatureStatus a ->
- m (TeamFeatureStatus a)
-setFeatureStatusNoConfig tid status = do
- let flag = Public.tfwoStatus status
- retry x5 $ write insert (params LocalQuorum (tid, flag))
- pure status
- where
- insert :: PrepQuery W (TeamId, TeamFeatureStatusValue) ()
- insert = fromString $ "insert into team_features (team_id, " <> statusCol @a <> ") values (?, ?)"
-
-getApplockFeatureStatus ::
- forall m.
- (MonadClient m) =>
- TeamId ->
- m (Maybe (TeamFeatureStatus 'Public.TeamFeatureAppLock))
-getApplockFeatureStatus tid = do
- let q = query1 select (params LocalQuorum (Identity tid))
- mTuple <- retry x1 q
- pure $
- mTuple >>= \(mbStatusValue, mbEnforce, mbTimeout) ->
- TeamFeatureStatusWithConfig <$> mbStatusValue <*> (Public.TeamFeatureAppLockConfig <$> mbEnforce <*> mbTimeout)
- where
- select :: PrepQuery R (Identity TeamId) (Maybe TeamFeatureStatusValue, Maybe Public.EnforceAppLock, Maybe Int32)
- select =
- fromString $
- "select " <> statusCol @'Public.TeamFeatureAppLock <> ", app_lock_enforce, app_lock_inactivity_timeout_secs "
- <> "from team_features where team_id = ?"
-
-setApplockFeatureStatus ::
- (MonadClient m) =>
- TeamId ->
- TeamFeatureStatus 'Public.TeamFeatureAppLock ->
- m (TeamFeatureStatus 'Public.TeamFeatureAppLock)
-setApplockFeatureStatus tid status = do
- let statusValue = Public.tfwcStatus status
- enforce = Public.applockEnforceAppLock . Public.tfwcConfig $ status
- timeout = Public.applockInactivityTimeoutSecs . Public.tfwcConfig $ status
- retry x5 $ write insert (params LocalQuorum (tid, statusValue, enforce, timeout))
- pure status
- where
- insert :: PrepQuery W (TeamId, TeamFeatureStatusValue, Public.EnforceAppLock, Int32) ()
- insert =
- fromString $
- "insert into team_features (team_id, "
- <> statusCol @'Public.TeamFeatureAppLock
- <> ", app_lock_enforce, app_lock_inactivity_timeout_secs) values (?, ?, ?, ?)"
-
-getSelfDeletingMessagesStatus ::
- forall m.
- (MonadClient m) =>
- TeamId ->
- m (Maybe (TeamFeatureStatus 'Public.TeamFeatureSelfDeletingMessages))
-getSelfDeletingMessagesStatus tid = do
- let q = query1 select (params LocalQuorum (Identity tid))
- mTuple <- retry x1 q
- pure $
- mTuple >>= \(mbStatusValue, mbTimeout) ->
- TeamFeatureStatusWithConfig <$> mbStatusValue <*> (Public.TeamFeatureSelfDeletingMessagesConfig <$> mbTimeout)
- where
- select :: PrepQuery R (Identity TeamId) (Maybe TeamFeatureStatusValue, Maybe Int32)
- select =
- fromString $
- "select "
- <> statusCol @'Public.TeamFeatureSelfDeletingMessages
- <> ", self_deleting_messages_ttl "
- <> "from team_features where team_id = ?"
-
-setSelfDeletingMessagesStatus ::
- (MonadClient m) =>
- TeamId ->
- TeamFeatureStatus 'Public.TeamFeatureSelfDeletingMessages ->
- m (TeamFeatureStatus 'Public.TeamFeatureSelfDeletingMessages)
-setSelfDeletingMessagesStatus tid status = do
- let statusValue = Public.tfwcStatus status
- timeout = Public.sdmEnforcedTimeoutSeconds . Public.tfwcConfig $ status
- retry x5 $ write insert (params LocalQuorum (tid, statusValue, timeout))
- pure status
- where
- insert :: PrepQuery W (TeamId, TeamFeatureStatusValue, Int32) ()
- insert =
- fromString $
- "insert into team_features (team_id, "
- <> statusCol @'Public.TeamFeatureSelfDeletingMessages
- <> ", self_deleting_messages_ttl) "
- <> "values (?, ?, ?)"
diff --git a/services/galley/src/Galley/Data/TeamNotifications.hs b/services/galley/src/Galley/Data/TeamNotifications.hs
index d7b3a9003d..faeff5edd9 100644
--- a/services/galley/src/Galley/Data/TeamNotifications.hs
+++ b/services/galley/src/Galley/Data/TeamNotifications.hs
@@ -23,21 +23,9 @@
--
-- FUTUREWORK: this is a work-around because it only solves *some* problems with team events.
-- We should really use a scalable message queue instead.
-module Galley.Data.TeamNotifications
- ( ResultPage (..),
- add,
- fetch,
- )
-where
+module Galley.Data.TeamNotifications (ResultPage (..)) where
-import Cassandra as C
-import qualified Data.Aeson as JSON
-import Data.Id
-import Data.List1 (List1)
-import Data.Range (Range, fromRange)
-import Data.Sequence (Seq, ViewL (..), ViewR (..), (<|), (><))
-import qualified Data.Sequence as Seq
-import Galley.App
+import Data.Sequence (Seq)
import Gundeck.Types.Notification
import Imports
@@ -49,92 +37,3 @@ data ResultPage = ResultPage
-- last notification in 'resultSeq'.
resultHasMore :: !Bool
}
-
--- FUTUREWORK: the magic 32 should be made configurable, so it can be tuned
-add ::
- TeamId ->
- NotificationId ->
- List1 JSON.Object ->
- Galley r ()
-add tid nid (Blob . JSON.encode -> payload) =
- write cqlInsert (params LocalQuorum (tid, nid, payload, notificationTTLSeconds)) & retry x5
- where
- cqlInsert :: PrepQuery W (TeamId, NotificationId, Blob, Int32) ()
- cqlInsert =
- "INSERT INTO team_notifications \
- \(team, id, payload) VALUES \
- \(?, ?, ?) \
- \USING TTL ?"
-
-notificationTTLSeconds :: Int32
-notificationTTLSeconds = 24192200
-
-fetch :: TeamId -> Maybe NotificationId -> Range 1 10000 Int32 -> Galley r ResultPage
-fetch tid since (fromRange -> size) = do
- -- We always need to look for one more than requested in order to correctly
- -- report whether there are more results.
- let size' = bool (+ 1) (+ 2) (isJust since) size
- page1 <- case TimeUuid . toUUID <$> since of
- Nothing -> paginate cqlStart (paramsP LocalQuorum (Identity tid) size') & retry x1
- Just s -> paginate cqlSince (paramsP LocalQuorum (tid, s) size') & retry x1
- -- Collect results, requesting more pages until we run out of data
- -- or have found size + 1 notifications (not including the 'since').
- let isize = fromIntegral size' :: Int
- (ns, more) <- collect Seq.empty isize page1
- -- Drop the extra element from the end as well. Keep the inclusive start
- -- value in the response (if a 'since' was given and found).
- -- This can probably simplified a lot further, but we need to understand
- -- 'Seq' in order to do that. If you find a bug, this may be a good
- -- place to start looking.
- return $! case Seq.viewl (trim (isize - 1) ns) of
- EmptyL -> ResultPage Seq.empty False
- (x :< xs) -> ResultPage (x <| xs) more
- where
- collect ::
- Seq QueuedNotification ->
- Int ->
- Page (TimeUuid, Blob) ->
- Galley r (Seq QueuedNotification, Bool)
- collect acc num page =
- let ns = splitAt num $ foldr toNotif [] (result page)
- nseq = Seq.fromList (fst ns)
- more = hasMore page
- num' = num - Seq.length nseq
- acc' = acc >< nseq
- in if not more || num' == 0
- then return (acc', more || not (null (snd ns)))
- else liftClient (nextPage page) >>= collect acc' num'
- trim :: Int -> Seq a -> Seq a
- trim l ns
- | Seq.length ns <= l = ns
- | otherwise = case Seq.viewr ns of
- EmptyR -> ns
- xs :> _ -> xs
- cqlStart :: PrepQuery R (Identity TeamId) (TimeUuid, Blob)
- cqlStart =
- "SELECT id, payload \
- \FROM team_notifications \
- \WHERE team = ? \
- \ORDER BY id ASC"
- cqlSince :: PrepQuery R (TeamId, TimeUuid) (TimeUuid, Blob)
- cqlSince =
- "SELECT id, payload \
- \FROM team_notifications \
- \WHERE team = ? AND id >= ? \
- \ORDER BY id ASC"
-
--------------------------------------------------------------------------------
--- Conversions
-
-toNotif :: (TimeUuid, Blob) -> [QueuedNotification] -> [QueuedNotification]
-toNotif (i, b) ns =
- maybe
- ns
- (\p1 -> queuedNotification notifId p1 : ns)
- ( JSON.decode' (fromBlob b)
- -- FUTUREWORK: this is from the database, so it's slightly more ok to ignore parse
- -- errors than if it's data provided by a client. it would still be better to have an
- -- error entry in the log file and crash, rather than ignore the error and continue.
- )
- where
- notifId = Id (fromTimeUuid i)
diff --git a/services/galley/src/Galley/Effects.hs b/services/galley/src/Galley/Effects.hs
index 4058b1ea44..e2f26dea92 100644
--- a/services/galley/src/Galley/Effects.hs
+++ b/services/galley/src/Galley/Effects.hs
@@ -36,10 +36,15 @@ module Galley.Effects
ClientStore,
CodeStore,
ConversationStore,
+ CustomBackendStore,
+ LegalHoldStore,
MemberStore,
+ SearchVisibilityStore,
ServiceStore,
- TeamStore,
+ TeamFeatureStore,
TeamMemberStore,
+ TeamNotificationStore,
+ TeamStore,
-- * Paging effects
ListItems,
@@ -58,15 +63,20 @@ import Galley.Effects.BrigAccess
import Galley.Effects.ClientStore
import Galley.Effects.CodeStore
import Galley.Effects.ConversationStore
+import Galley.Effects.CustomBackendStore
import Galley.Effects.ExternalAccess
import Galley.Effects.FederatorAccess
import Galley.Effects.FireAndForget
import Galley.Effects.GundeckAccess
+import Galley.Effects.LegalHoldStore
import Galley.Effects.ListItems
import Galley.Effects.MemberStore
+import Galley.Effects.SearchVisibilityStore
import Galley.Effects.ServiceStore
import Galley.Effects.SparAccess
+import Galley.Effects.TeamFeatureStore
import Galley.Effects.TeamMemberStore
+import Galley.Effects.TeamNotificationStore
import Galley.Effects.TeamStore
import Polysemy
@@ -82,8 +92,13 @@ type GalleyEffects1 =
ClientStore,
CodeStore,
ConversationStore,
+ CustomBackendStore,
+ LegalHoldStore,
MemberStore,
+ SearchVisibilityStore,
ServiceStore,
+ TeamFeatureStore,
+ TeamNotificationStore,
TeamStore,
TeamMemberStore InternalPaging,
ListItems CassandraPaging ConvId,
diff --git a/services/galley/src/Galley/Effects/CustomBackendStore.hs b/services/galley/src/Galley/Effects/CustomBackendStore.hs
new file mode 100644
index 0000000000..cd3fc72300
--- /dev/null
+++ b/services/galley/src/Galley/Effects/CustomBackendStore.hs
@@ -0,0 +1,36 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2021 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+module Galley.Effects.CustomBackendStore
+ ( CustomBackendStore (..),
+ getCustomBackend,
+ setCustomBackend,
+ deleteCustomBackend,
+ )
+where
+
+import Data.Domain (Domain)
+import Galley.Types
+import Imports
+import Polysemy
+
+data CustomBackendStore m a where
+ GetCustomBackend :: Domain -> CustomBackendStore m (Maybe CustomBackend)
+ SetCustomBackend :: Domain -> CustomBackend -> CustomBackendStore m ()
+ DeleteCustomBackend :: Domain -> CustomBackendStore m ()
+
+makeSem ''CustomBackendStore
diff --git a/services/galley/src/Galley/Effects/LegalHoldStore.hs b/services/galley/src/Galley/Effects/LegalHoldStore.hs
new file mode 100644
index 0000000000..28b70fcf1f
--- /dev/null
+++ b/services/galley/src/Galley/Effects/LegalHoldStore.hs
@@ -0,0 +1,52 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2021 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+module Galley.Effects.LegalHoldStore
+ ( LegalHoldStore (..),
+ createSettings,
+ getSettings,
+ removeSettings,
+ insertPendingPrekeys,
+ selectPendingPrekeys,
+ dropPendingPrekeys,
+ setUserLegalHoldStatus,
+ setTeamLegalholdWhitelisted,
+ unsetTeamLegalholdWhitelisted,
+ isTeamLegalholdWhitelisted,
+ )
+where
+
+import Data.Id
+import Data.LegalHold
+import Galley.External.LegalHoldService.Types
+import Imports
+import Polysemy
+import Wire.API.User.Client.Prekey
+
+data LegalHoldStore m a where
+ CreateSettings :: LegalHoldService -> LegalHoldStore m ()
+ GetSettings :: TeamId -> LegalHoldStore m (Maybe LegalHoldService)
+ RemoveSettings :: TeamId -> LegalHoldStore m ()
+ InsertPendingPrekeys :: UserId -> [Prekey] -> LegalHoldStore m ()
+ SelectPendingPrekeys :: UserId -> LegalHoldStore m (Maybe ([Prekey], LastPrekey))
+ DropPendingPrekeys :: UserId -> LegalHoldStore m ()
+ SetUserLegalHoldStatus :: TeamId -> UserId -> UserLegalHoldStatus -> LegalHoldStore m ()
+ SetTeamLegalholdWhitelisted :: TeamId -> LegalHoldStore m ()
+ UnsetTeamLegalholdWhitelisted :: TeamId -> LegalHoldStore m ()
+ IsTeamLegalholdWhitelisted :: TeamId -> LegalHoldStore m Bool
+
+makeSem ''LegalHoldStore
diff --git a/services/galley/src/Galley/Effects/SearchVisibilityStore.hs b/services/galley/src/Galley/Effects/SearchVisibilityStore.hs
new file mode 100644
index 0000000000..28a9b394c3
--- /dev/null
+++ b/services/galley/src/Galley/Effects/SearchVisibilityStore.hs
@@ -0,0 +1,35 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2021 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+module Galley.Effects.SearchVisibilityStore
+ ( SearchVisibilityStore (..),
+ getSearchVisibility,
+ setSearchVisibility,
+ resetSearchVisibility,
+ )
+where
+
+import Data.Id
+import Galley.Types.Teams.SearchVisibility
+import Polysemy
+
+data SearchVisibilityStore m a where
+ GetSearchVisibility :: TeamId -> SearchVisibilityStore m TeamSearchVisibility
+ SetSearchVisibility :: TeamId -> TeamSearchVisibility -> SearchVisibilityStore m ()
+ ResetSearchVisibility :: TeamId -> SearchVisibilityStore m ()
+
+makeSem ''SearchVisibilityStore
diff --git a/services/galley/src/Galley/Effects/TeamFeatureStore.hs b/services/galley/src/Galley/Effects/TeamFeatureStore.hs
new file mode 100644
index 0000000000..d2910980f2
--- /dev/null
+++ b/services/galley/src/Galley/Effects/TeamFeatureStore.hs
@@ -0,0 +1,86 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2021 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+module Galley.Effects.TeamFeatureStore
+ ( TeamFeatureStore (..),
+ getFeatureStatusNoConfig,
+ setFeatureStatusNoConfig,
+ getApplockFeatureStatus,
+ setApplockFeatureStatus,
+ getSelfDeletingMessagesStatus,
+ setSelfDeletingMessagesStatus,
+ )
+where
+
+import Data.Id
+import Data.Proxy
+import Galley.Data.TeamFeatures
+import Imports
+import Polysemy
+import Wire.API.Team.Feature
+
+data TeamFeatureStore m a where
+ -- the proxy argument makes sure that makeSem below generates type-inference-friendly code
+ GetFeatureStatusNoConfig' ::
+ forall (a :: TeamFeatureName) m.
+ ( FeatureHasNoConfig a,
+ HasStatusCol a
+ ) =>
+ Proxy a ->
+ TeamId ->
+ TeamFeatureStore m (Maybe (TeamFeatureStatus a))
+ -- the proxy argument makes sure that makeSem below generates type-inference-friendly code
+ SetFeatureStatusNoConfig' ::
+ forall (a :: TeamFeatureName) m.
+ ( FeatureHasNoConfig a,
+ HasStatusCol a
+ ) =>
+ Proxy a ->
+ TeamId ->
+ TeamFeatureStatus a ->
+ TeamFeatureStore m (TeamFeatureStatus a)
+ GetApplockFeatureStatus ::
+ TeamId ->
+ TeamFeatureStore m (Maybe (TeamFeatureStatus 'TeamFeatureAppLock))
+ SetApplockFeatureStatus ::
+ TeamId ->
+ TeamFeatureStatus 'TeamFeatureAppLock ->
+ TeamFeatureStore m (TeamFeatureStatus 'TeamFeatureAppLock)
+ GetSelfDeletingMessagesStatus ::
+ TeamId ->
+ TeamFeatureStore m (Maybe (TeamFeatureStatus 'TeamFeatureSelfDeletingMessages))
+ SetSelfDeletingMessagesStatus ::
+ TeamId ->
+ TeamFeatureStatus 'TeamFeatureSelfDeletingMessages ->
+ TeamFeatureStore m (TeamFeatureStatus 'TeamFeatureSelfDeletingMessages)
+
+makeSem ''TeamFeatureStore
+
+getFeatureStatusNoConfig ::
+ forall (a :: TeamFeatureName) r.
+ (Member TeamFeatureStore r, FeatureHasNoConfig a, HasStatusCol a) =>
+ TeamId ->
+ Sem r (Maybe (TeamFeatureStatus a))
+getFeatureStatusNoConfig = getFeatureStatusNoConfig' (Proxy @a)
+
+setFeatureStatusNoConfig ::
+ forall (a :: TeamFeatureName) r.
+ (Member TeamFeatureStore r, FeatureHasNoConfig a, HasStatusCol a) =>
+ TeamId ->
+ TeamFeatureStatus a ->
+ Sem r (TeamFeatureStatus a)
+setFeatureStatusNoConfig = setFeatureStatusNoConfig' (Proxy @a)
diff --git a/services/galley/src/Galley/Effects/TeamNotificationStore.hs b/services/galley/src/Galley/Effects/TeamNotificationStore.hs
new file mode 100644
index 0000000000..5e553315d4
--- /dev/null
+++ b/services/galley/src/Galley/Effects/TeamNotificationStore.hs
@@ -0,0 +1,41 @@
+-- This file is part of the Wire Server implementation.
+--
+-- Copyright (C) 2021 Wire Swiss GmbH
+--
+-- This program is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU Affero General Public License as published by the Free
+-- Software Foundation, either version 3 of the License, or (at your option) any
+-- later version.
+--
+-- This program is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+-- details.
+--
+-- You should have received a copy of the GNU Affero General Public License along
+-- with this program. If not, see .
+
+module Galley.Effects.TeamNotificationStore where
+
+import qualified Data.Aeson as JSON
+import Data.Id
+import Data.List1 (List1)
+import Data.Range
+import Galley.Data.TeamNotifications
+import Gundeck.Types.Notification
+import Imports
+import Polysemy
+
+data TeamNotificationStore m a where
+ CreateTeamNotification ::
+ TeamId ->
+ NotificationId ->
+ List1 JSON.Object ->
+ TeamNotificationStore m ()
+ GetTeamNotifications ::
+ TeamId ->
+ Maybe NotificationId ->
+ Range 1 10000 Int32 ->
+ TeamNotificationStore m ResultPage
+
+makeSem ''TeamNotificationStore
diff --git a/services/galley/src/Galley/External/LegalHoldService.hs b/services/galley/src/Galley/External/LegalHoldService.hs
index ac503fc67c..1c4a3038ec 100644
--- a/services/galley/src/Galley/External/LegalHoldService.hs
+++ b/services/galley/src/Galley/External/LegalHoldService.hs
@@ -44,7 +44,7 @@ import Data.Id
import Data.Misc
import Galley.API.Error
import Galley.App
-import qualified Galley.Data.LegalHold as LegalHoldData
+import Galley.Effects.LegalHoldStore as LegalHoldData
import Galley.Env
import Galley.External.LegalHoldService.Types
import Imports
@@ -55,6 +55,7 @@ import qualified OpenSSL.EVP.PKey as SSL
import qualified OpenSSL.PEM as SSL
import qualified OpenSSL.RSA as SSL
import qualified OpenSSL.Session as SSL
+import Polysemy
import Ssl.Util
import qualified Ssl.Util as SSL
import qualified System.Logger.Class as Log
@@ -80,7 +81,11 @@ checkLegalHoldServiceStatus fpr url = do
. Bilge.expect2xx
-- | @POST /initiate@.
-requestNewDevice :: TeamId -> UserId -> Galley r NewLegalHoldClient
+requestNewDevice ::
+ Member LegalHoldStore r =>
+ TeamId ->
+ UserId ->
+ Galley r NewLegalHoldClient
requestNewDevice tid uid = do
resp <- makeLegalHoldServiceRequest tid reqParams
case eitherDecode (responseBody resp) of
@@ -99,6 +104,7 @@ requestNewDevice tid uid = do
-- | @POST /confirm@
-- Confirm that a device has been linked to a user and provide an authorization token
confirmLegalHold ::
+ Member LegalHoldStore r =>
ClientId ->
TeamId ->
UserId ->
@@ -118,6 +124,7 @@ confirmLegalHold clientId tid uid legalHoldAuthToken = do
-- | @POST /remove@
-- Inform the LegalHold Service that a user's legalhold has been disabled.
removeLegalHold ::
+ Member LegalHoldStore r =>
TeamId ->
UserId ->
Galley r ()
@@ -137,9 +144,13 @@ removeLegalHold tid uid = do
-- | Lookup legal hold service settings for a team and make a request to the service. Pins
-- the TSL fingerprint via 'makeVerifiedRequest' and passes the token so the service can
-- authenticate the request.
-makeLegalHoldServiceRequest :: TeamId -> (Http.Request -> Http.Request) -> Galley r (Http.Response LC8.ByteString)
+makeLegalHoldServiceRequest ::
+ Member LegalHoldStore r =>
+ TeamId ->
+ (Http.Request -> Http.Request) ->
+ Galley r (Http.Response LC8.ByteString)
makeLegalHoldServiceRequest tid reqBuilder = do
- maybeLHSettings <- LegalHoldData.getSettings tid
+ maybeLHSettings <- liftSem $ LegalHoldData.getSettings tid
lhSettings <- case maybeLHSettings of
Nothing -> throwM legalHoldServiceNotRegistered
Just lhSettings -> pure lhSettings
diff --git a/services/galley/src/Galley/External/LegalHoldService/Types.hs b/services/galley/src/Galley/External/LegalHoldService/Types.hs
index cecf37ad87..8a3f671bcf 100644
--- a/services/galley/src/Galley/External/LegalHoldService/Types.hs
+++ b/services/galley/src/Galley/External/LegalHoldService/Types.hs
@@ -19,9 +19,13 @@
module Galley.External.LegalHoldService.Types
( OpaqueAuthToken (..),
+
+ -- * Re-exports
+ LegalHoldService,
)
where
+import Brig.Types.Team.LegalHold
import Data.Aeson
import Data.ByteString.Conversion.To
import Imports
diff --git a/services/galley/test/integration/API/Teams/LegalHold.hs b/services/galley/test/integration/API/Teams/LegalHold.hs
index 2f16338b5a..b5d4cd24de 100644
--- a/services/galley/test/integration/API/Teams/LegalHold.hs
+++ b/services/galley/test/integration/API/Teams/LegalHold.hs
@@ -66,7 +66,7 @@ import Data.Text.Encoding (encodeUtf8)
import qualified Data.Time.Clock as Time
import qualified Galley.App as Galley
import Galley.Cassandra.Client
-import qualified Galley.Data.LegalHold as LegalHoldData
+import qualified Galley.Cassandra.LegalHold as LegalHoldData
import Galley.External.LegalHoldService (validateServiceKey)
import Galley.Options (optSettings, setFeatureFlags)
import qualified Galley.Types.Clients as Clients
diff --git a/services/galley/test/integration/API/Teams/LegalHold/DisabledByDefault.hs b/services/galley/test/integration/API/Teams/LegalHold/DisabledByDefault.hs
index c9ee4e914a..f7ce810822 100644
--- a/services/galley/test/integration/API/Teams/LegalHold/DisabledByDefault.hs
+++ b/services/galley/test/integration/API/Teams/LegalHold/DisabledByDefault.hs
@@ -60,7 +60,7 @@ import Data.String.Conversions (LBS, cs)
import Data.Text.Encoding (encodeUtf8)
import qualified Galley.App as Galley
import Galley.Cassandra.Client
-import qualified Galley.Data.LegalHold as LegalHoldData
+import qualified Galley.Cassandra.LegalHold as LegalHoldData
import Galley.External.LegalHoldService (validateServiceKey)
import Galley.Options (optSettings, setFeatureFlags)
import qualified Galley.Types.Clients as Clients
diff --git a/tools/db/migrate-sso-feature-flag/src/Work.hs b/tools/db/migrate-sso-feature-flag/src/Work.hs
index c2dc980ab7..582f2fdf3b 100644
--- a/tools/db/migrate-sso-feature-flag/src/Work.hs
+++ b/tools/db/migrate-sso-feature-flag/src/Work.hs
@@ -31,7 +31,7 @@ import Data.Conduit.Internal (zipSources)
import qualified Data.Conduit.List as C
import Data.Id
import Data.Misc
-import Galley.Data.Instances ()
+import Galley.Cassandra.Instances ()
import Imports
import System.Logger (Logger)
import qualified System.Logger as Log
diff --git a/tools/db/move-team/src/ParseSchema.hs b/tools/db/move-team/src/ParseSchema.hs
index cae5abef31..7d4299a8b2 100644
--- a/tools/db/move-team/src/ParseSchema.hs
+++ b/tools/db/move-team/src/ParseSchema.hs
@@ -205,7 +205,7 @@ import Data.Conduit
import Data.Id
import Data.Time
import Data.UUID
-import Galley.Data.Instances ()
+import Galley.Cassandra.Instances ()
import Imports
import Types
import Wire.API.Team.Permission
diff --git a/tools/db/move-team/src/Schema.hs b/tools/db/move-team/src/Schema.hs
index 56ce0a9a8d..1958fb705b 100644
--- a/tools/db/move-team/src/Schema.hs
+++ b/tools/db/move-team/src/Schema.hs
@@ -27,7 +27,7 @@ import Data.IP (IP)
import Data.Id
import Data.Time
import Data.UUID
-import Galley.Data.Instances ()
+import Galley.Cassandra.Instances ()
import Imports
import System.FilePath.Posix ((>))
import Types
diff --git a/tools/db/move-team/src/Types.hs b/tools/db/move-team/src/Types.hs
index 8d079316b0..e103908cb7 100644
--- a/tools/db/move-team/src/Types.hs
+++ b/tools/db/move-team/src/Types.hs
@@ -36,7 +36,7 @@ import Data.Id
import qualified Data.Text as T
import Data.Text.Ascii (AsciiText, Base64, decodeBase64, encodeBase64)
import qualified Data.Vector as V
-import Galley.Data.Instances ()
+import Galley.Cassandra.Instances ()
import Imports
import System.Logger (Logger)
import Wire.API.User.Password (PasswordResetKey (..))
diff --git a/tools/db/move-team/src/Work.hs b/tools/db/move-team/src/Work.hs
index 7d9e8207ae..cab4883b38 100644
--- a/tools/db/move-team/src/Work.hs
+++ b/tools/db/move-team/src/Work.hs
@@ -38,7 +38,7 @@ import qualified Data.Conduit.List as CL
import Data.Id
import qualified Data.Set as Set
import Data.UUID
-import Galley.Data.Instances ()
+import Galley.Cassandra.Instances ()
import Imports
import Schema
import System.Exit (ExitCode (ExitFailure, ExitSuccess), exitWith)