From ad5b5240d8ea77e22314ea8b74ce652cd266820c Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Mon, 16 Jan 2023 11:22:31 +0100 Subject: [PATCH 01/12] Expose swagger docs for brig's internal (servantified) endpoints Start the ToSchema party with brig and cannon Remove unnecessary module qualifier Fix type error Fix Create instance `ToSchema List1` Add swagger tags (resulting in sections) Replace over with %~ Introduce SwaggerTag combinator Use lens Add some documentation text Test RawJson FromJSON/ToJSON Cleanup Delete unused endpoint Align title with cannon's swagger title Delete Won't-do ToDos Add changelog Remove superfluous comment Add docs about Swagger Describe the swagger URL pattern better Use versions in swagger endpoint Update docs Move example section Fix typo Update swagger.md Old Swagger is now gone. Update services/brig/src/Brig/API/Public.hs Co-authored-by: fisx Update services/brig/docs/swagger-internal-endpoints.md Co-authored-by: fisx Update changelog.d/2-features/internal-endpoints-swagger Co-authored-by: fisx Update libs/wire-api/src/Wire/API/SwaggerServant.hs Co-authored-by: fisx Update libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs Co-authored-by: fisx Remove redundant constraint Simplify operations tagging with allOperations Traversal Use LText as data store of RawJson Replace ToSchema instance with function Increase lazy-ness of ToSchema RawJson instance Remove ToSchema for internal data structures Swagger docs for internal endpoints of Legalhold and Cargohold Move galley's internal endpoint to wire-api Swagger for internal Galley endpoints Add spar's internal endpoints --- libs/galley-types/galley-types.cabal | 2 - .../test/unit/Test/Galley/Types.hs | 2 - libs/types-common/src/Data/Json/Util.hs | 10 + .../src/Wire/API/Routes/Internal/Cargohold.hs | 15 +- .../src/Wire/API/Routes/Internal/Galley.hs | 395 ++++++++++++++++++ .../Internal/Galley/ConversationsIntra.hs} | 24 +- .../Galley/TeamFeatureNoConfigMulti.hs | 4 +- .../API/Routes/Internal/Galley/TeamsIntra.hs} | 43 +- .../src/Wire/API/Routes/Internal/LegalHold.hs | 31 +- .../src/Wire/API/Routes/Internal/Spar.hs | 25 ++ .../src/Wire/API/Routes/Public/Spar.hs | 9 +- libs/wire-api/src/Wire/API/Team/Feature.hs | 6 +- libs/wire-api/src/Wire/API/Team/LegalHold.hs | 48 ++- .../unit/Test/Wire/API/Roundtrip/Aeson.hs | 4 +- libs/wire-api/wire-api.cabal | 4 + .../brig/src/Brig/API/Connection/Remote.hs | 2 +- services/brig/src/Brig/API/Public.hs | 10 +- services/brig/src/Brig/API/User.hs | 2 +- .../brig/src/Brig/Effects/GalleyProvider.hs | 2 +- .../src/Brig/Effects/GalleyProvider/RPC.hs | 2 +- services/brig/src/Brig/IO/Intra.hs | 4 +- services/brig/src/Brig/Team/API.hs | 2 +- services/brig/test/integration/API/Team.hs | 2 +- .../brig/test/integration/API/Team/Util.hs | 2 +- services/galley/src/Galley/API/Internal.hs | 376 +---------------- services/galley/src/Galley/API/One2One.hs | 2 +- services/galley/src/Galley/API/Teams.hs | 2 +- .../galley/src/Galley/Cassandra/Instances.hs | 2 +- .../galley/src/Galley/Cassandra/Queries.hs | 2 +- services/galley/src/Galley/Cassandra/Team.hs | 2 +- .../galley/src/Galley/Effects/TeamStore.hs | 2 +- services/galley/test/integration/API.hs | 2 +- .../galley/test/integration/API/Federation.hs | 2 +- services/galley/test/integration/API/Teams.hs | 2 +- services/galley/test/integration/API/Util.hs | 4 +- services/spar/src/Spar/API.hs | 1 + tools/stern/src/Stern/API.hs | 2 +- tools/stern/src/Stern/Intra.hs | 4 +- tools/stern/src/Stern/Types.hs | 2 +- 39 files changed, 588 insertions(+), 469 deletions(-) create mode 100644 libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs rename libs/{galley-types/src/Galley/Types/Conversations/Intra.hs => wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs} (68%) rename libs/{galley-types/src/Galley/Types/Teams/Intra.hs => wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs} (76%) create mode 100644 libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs diff --git a/libs/galley-types/galley-types.cabal b/libs/galley-types/galley-types.cabal index 0dccd7d2a3..005902f1d9 100644 --- a/libs/galley-types/galley-types.cabal +++ b/libs/galley-types/galley-types.cabal @@ -15,12 +15,10 @@ library Galley.Types Galley.Types.Bot Galley.Types.Bot.Service - Galley.Types.Conversations.Intra Galley.Types.Conversations.Members Galley.Types.Conversations.One2One Galley.Types.Conversations.Roles Galley.Types.Teams - Galley.Types.Teams.Intra other-modules: Paths_galley_types hs-source-dirs: src diff --git a/libs/galley-types/test/unit/Test/Galley/Types.hs b/libs/galley-types/test/unit/Test/Galley/Types.hs index 367f0413f1..b66e4ded3d 100644 --- a/libs/galley-types/test/unit/Test/Galley/Types.hs +++ b/libs/galley-types/test/unit/Test/Galley/Types.hs @@ -24,7 +24,6 @@ import Control.Lens import Data.Set hiding (drop) import qualified Data.Set as Set import Galley.Types.Teams -import Galley.Types.Teams.Intra (GuardLegalholdPolicyConflicts) import Imports import Test.Galley.Roundtrip (testRoundTrip) import qualified Test.QuickCheck as QC @@ -49,7 +48,6 @@ tests = assertBool "owner.self" ((rolePermissions r2 ^. self) `isSubsetOf` (rolePermissions r1 ^. self)) assertBool "owner.copy" ((rolePermissions r2 ^. copy) `isSubsetOf` (rolePermissions r1 ^. copy)), testRoundTrip @FeatureFlags, - testRoundTrip @GuardLegalholdPolicyConflicts, testGroup "permissionsRole, rolePermissions" [ testCase "'Role' maps to expected permissions" $ do diff --git a/libs/types-common/src/Data/Json/Util.hs b/libs/types-common/src/Data/Json/Util.hs index 92ba6bb8e4..edf41b9c60 100644 --- a/libs/types-common/src/Data/Json/Util.hs +++ b/libs/types-common/src/Data/Json/Util.hs @@ -61,6 +61,7 @@ import qualified Data.ByteString.Base64.URL as B64U import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Conversion as BS import qualified Data.ByteString.Lazy as L +import qualified Data.Currency as Currency import Data.Fixed import Data.Schema import Data.String.Conversions (cs) @@ -269,3 +270,12 @@ fromBase64Text = B64.decode . Text.encodeUtf8 toBase64Text :: ByteString -> Text toBase64Text = Text.decodeUtf8 . B64.encode + +-- TODO: Find a better module for this +instance ToSchema Currency.Alpha where + schema = mkSchema docs parseJSON (pure . toJSON) + where + docs = + swaggerDoc @Text + & S.schema . S.description ?~ "ISO 4217 alphabetic codes" + & S.schema . S.example ?~ "EUR" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs index 5900b31d96..de11695246 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs @@ -17,8 +17,21 @@ module Wire.API.Routes.Internal.Cargohold where +import Control.Lens +import Data.Swagger +import Imports import Servant +import Servant.Swagger import Wire.API.Routes.MultiVerb +import Wire.API.SwaggerServant type InternalAPI = - "i" :> "status" :> MultiVerb 'GET '() '[RespondEmpty 200 "OK"] () + SwaggerTag "cargohold" + :> "i" + :> "status" + :> MultiVerb 'GET '() '[RespondEmpty 200 "OK"] () + +swaggerDoc :: Swagger +swaggerDoc = + toSwagger (Proxy @InternalAPI) + & info . title .~ "Wire-Server internal cargohold API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs new file mode 100644 index 0000000000..1bae089ea6 --- /dev/null +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs @@ -0,0 +1,395 @@ +module Wire.API.Routes.Internal.Galley where + +import Control.Lens ((.~)) +import Data.Id as Id +import Data.Range +import Data.Swagger (Swagger, info, title) +import GHC.TypeLits (AppendSymbol) +import Imports hiding (head) +import Servant hiding (JSON, WithStatus) +import qualified Servant hiding (WithStatus) +import Servant.Swagger +import Wire.API.ApplyMods +import Wire.API.Conversation.Role +import Wire.API.Error +import Wire.API.Error.Galley +import Wire.API.Event.Conversation +import Wire.API.MakesFederatedCall +import Wire.API.Routes.Internal.Galley.ConversationsIntra +import Wire.API.Routes.Internal.Galley.TeamFeatureNoConfigMulti +import Wire.API.Routes.Internal.Galley.TeamsIntra +import Wire.API.Routes.MultiVerb +import Wire.API.Routes.Named +import Wire.API.Routes.Public +import Wire.API.Routes.Public.Galley.Conversation +import Wire.API.Routes.Public.Galley.Feature +import Wire.API.SwaggerServant +import Wire.API.Team +import Wire.API.Team.Feature +import Wire.API.Team.Member +import Wire.API.Team.SearchVisibility + +type LegalHoldFeatureStatusChangeErrors = + '( 'ActionDenied 'RemoveConversationMember, + '( AuthenticationError, + '( 'CannotEnableLegalHoldServiceLargeTeam, + '( 'LegalHoldNotEnabled, + '( 'LegalHoldDisableUnimplemented, + '( 'LegalHoldServiceNotRegistered, + '( 'UserLegalHoldIllegalOperation, + '( 'LegalHoldCouldNotBlockConnections, '()) + ) + ) + ) + ) + ) + ) + ) + +type LegalHoldFeaturesStatusChangeFederatedCalls = + '[ MakesFederatedCall 'Galley "on-conversation-updated", + MakesFederatedCall 'Galley "on-mls-message-sent", + MakesFederatedCall 'Galley "on-new-remote-conversation" + ] + +type IFeatureAPI = + -- SSOConfig + IFeatureStatusGet SSOConfig + :<|> IFeatureStatusPut '[] '() SSOConfig + :<|> IFeatureStatusPatch '[] '() SSOConfig + -- LegalholdConfig + :<|> IFeatureStatusGet LegalholdConfig + :<|> IFeatureStatusPut + LegalHoldFeaturesStatusChangeFederatedCalls + LegalHoldFeatureStatusChangeErrors + LegalholdConfig + :<|> IFeatureStatusPatch + LegalHoldFeaturesStatusChangeFederatedCalls + LegalHoldFeatureStatusChangeErrors + LegalholdConfig + -- SearchVisibilityAvailableConfig + :<|> IFeatureStatusGet SearchVisibilityAvailableConfig + :<|> IFeatureStatusPut '[] '() SearchVisibilityAvailableConfig + :<|> IFeatureStatusPatch '[] '() SearchVisibilityAvailableConfig + -- ValidateSAMLEmailsConfig + :<|> IFeatureStatusGet ValidateSAMLEmailsConfig + :<|> IFeatureStatusPut '[] '() ValidateSAMLEmailsConfig + :<|> IFeatureStatusPatch '[] '() ValidateSAMLEmailsConfig + -- DigitalSignaturesConfig + :<|> IFeatureStatusGet DigitalSignaturesConfig + :<|> IFeatureStatusPut '[] '() DigitalSignaturesConfig + :<|> IFeatureStatusPatch '[] '() DigitalSignaturesConfig + -- AppLockConfig + :<|> IFeatureStatusGet AppLockConfig + :<|> IFeatureStatusPut '[] '() AppLockConfig + :<|> IFeatureStatusPatch '[] '() AppLockConfig + -- FileSharingConfig + :<|> IFeatureStatusGet FileSharingConfig + :<|> IFeatureStatusPut '[] '() FileSharingConfig + :<|> IFeatureStatusLockStatusPut FileSharingConfig + :<|> IFeatureStatusPatch '[] '() FileSharingConfig + -- ConferenceCallingConfig + :<|> IFeatureStatusGet ConferenceCallingConfig + :<|> IFeatureStatusPut '[] '() ConferenceCallingConfig + :<|> IFeatureStatusPatch '[] '() ConferenceCallingConfig + -- SelfDeletingMessagesConfig + :<|> IFeatureStatusGet SelfDeletingMessagesConfig + :<|> IFeatureStatusPut '[] '() SelfDeletingMessagesConfig + :<|> IFeatureStatusLockStatusPut SelfDeletingMessagesConfig + :<|> IFeatureStatusPatch '[] '() SelfDeletingMessagesConfig + -- GuestLinksConfig + :<|> IFeatureStatusGet GuestLinksConfig + :<|> IFeatureStatusPut '[] '() GuestLinksConfig + :<|> IFeatureStatusLockStatusPut GuestLinksConfig + :<|> IFeatureStatusPatch '[] '() GuestLinksConfig + -- SndFactorPasswordChallengeConfig + :<|> IFeatureStatusGet SndFactorPasswordChallengeConfig + :<|> IFeatureStatusPut '[] '() SndFactorPasswordChallengeConfig + :<|> IFeatureStatusLockStatusPut SndFactorPasswordChallengeConfig + :<|> IFeatureStatusPatch '[] '() SndFactorPasswordChallengeConfig + -- SearchVisibilityInboundConfig + :<|> IFeatureStatusGet SearchVisibilityInboundConfig + :<|> IFeatureStatusPut '[] '() SearchVisibilityInboundConfig + :<|> IFeatureStatusPatch '[] '() SearchVisibilityInboundConfig + :<|> IFeatureNoConfigMultiGet SearchVisibilityInboundConfig + -- ClassifiedDomainsConfig + :<|> IFeatureStatusGet ClassifiedDomainsConfig + -- MLSConfig + :<|> IFeatureStatusGet MLSConfig + :<|> IFeatureStatusPut '[] '() MLSConfig + :<|> IFeatureStatusPatch '[] '() MLSConfig + -- ExposeInvitationURLsToTeamAdminConfig + :<|> IFeatureStatusGet ExposeInvitationURLsToTeamAdminConfig + :<|> IFeatureStatusPut '[] '() ExposeInvitationURLsToTeamAdminConfig + :<|> IFeatureStatusPatch '[] '() ExposeInvitationURLsToTeamAdminConfig + -- SearchVisibilityInboundConfig + :<|> IFeatureStatusGet SearchVisibilityInboundConfig + :<|> IFeatureStatusPut '[] '() SearchVisibilityInboundConfig + :<|> IFeatureStatusPatch '[] '() SearchVisibilityInboundConfig + -- OutlookCalIntegrationConfig + :<|> IFeatureStatusGet OutlookCalIntegrationConfig + :<|> IFeatureStatusPut '[] '() OutlookCalIntegrationConfig + :<|> IFeatureStatusPatch '[] '() OutlookCalIntegrationConfig + :<|> IFeatureStatusLockStatusPut OutlookCalIntegrationConfig + -- all feature configs + :<|> Named + "feature-configs-internal" + ( Summary "Get all feature configs (for user/team; if n/a fall back to site config)." + :> "feature-configs" + :> CanThrow OperationDenied + :> CanThrow 'NotATeamMember + :> CanThrow 'TeamNotFound + :> QueryParam' + [ Optional, + Strict, + Description "Optional user id" + ] + "user_id" + UserId + :> Get '[Servant.JSON] AllFeatureConfigs + ) + +type InternalAPI = SwaggerTag "galley" :> "i" :> InternalAPIBase + +type InternalAPIBase = + Named + "status" + ( "status" :> MultiVerb 'GET '[Servant.JSON] '[RespondEmpty 200 "OK"] () + ) + -- This endpoint can lead to the following events being sent: + -- - MemberLeave event to members for all conversations the user was in + :<|> Named + "delete-user" + ( Summary + "Remove a user from their teams and conversations and erase their clients" + :> MakesFederatedCall 'Galley "on-conversation-updated" + :> MakesFederatedCall 'Galley "on-user-deleted-conversations" + :> MakesFederatedCall 'Galley "on-mls-message-sent" + :> ZLocalUser + :> ZOptConn + :> "user" + :> MultiVerb 'DELETE '[Servant.JSON] '[RespondEmpty 200 "Remove a user from Galley"] () + ) + -- This endpoint can lead to the following events being sent: + -- - ConvCreate event to self, if conversation did not exist before + -- - ConvConnect event to self, if other didn't join the connect conversation before + :<|> Named + "connect" + ( Summary "Create a connect conversation (deprecated)" + :> MakesFederatedCall 'Galley "on-conversation-created" + :> CanThrow 'ConvNotFound + :> CanThrow 'InvalidOperation + :> CanThrow 'NotConnected + :> ZLocalUser + :> ZOptConn + :> "conversations" + :> "connect" + :> ReqBody '[Servant.JSON] Connect + :> ConversationVerb + ) + :<|> Named + "guard-legalhold-policy-conflicts" + ( "guard-legalhold-policy-conflicts" + :> CanThrow 'MissingLegalholdConsent + :> ReqBody '[Servant.JSON] GuardLegalholdPolicyConflicts + :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "Guard Legalhold Policy") + ) + :<|> ILegalholdWhitelistedTeamsAPI + :<|> ITeamsAPI + :<|> Named + "upsert-one2one" + ( Summary "Create or Update a connect or one2one conversation." + :> "conversations" + :> "one2one" + :> "upsert" + :> ReqBody '[Servant.JSON] UpsertOne2OneConversationRequest + :> Post '[Servant.JSON] UpsertOne2OneConversationResponse + ) + :<|> IFeatureAPI + +type ILegalholdWhitelistedTeamsAPI = + "legalhold" + :> "whitelisted-teams" + :> Capture "tid" TeamId + :> ILegalholdWhitelistedTeamsAPIBase + +type ILegalholdWhitelistedTeamsAPIBase = + Named + "set-team-legalhold-whitelisted" + (MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "Team Legalhold Whitelisted")) + :<|> Named + "unset-team-legalhold-whitelisted" + (MultiVerb1 'DELETE '[Servant.JSON] (RespondEmpty 204 "Team Legalhold un-Whitelisted")) + :<|> Named + "get-team-legalhold-whitelisted" + ( MultiVerb + 'GET + '[Servant.JSON] + '[ RespondEmpty 404 "Team not Legalhold Whitelisted", + RespondEmpty 200 "Team Legalhold Whitelisted" + ] + Bool + ) + +type ITeamsAPI = "teams" :> Capture "tid" TeamId :> ITeamsAPIBase + +type ITeamsAPIBase = + Named "get-team-internal" (CanThrow 'TeamNotFound :> Get '[Servant.JSON] TeamData) + :<|> Named + "create-binding-team" + ( ZUser + :> ReqBody '[Servant.JSON] BindingNewTeam + :> MultiVerb1 + 'PUT + '[Servant.JSON] + ( WithHeaders + '[Header "Location" TeamId] + TeamId + (RespondEmpty 201 "OK") + ) + ) + :<|> Named + "delete-binding-team" + ( CanThrow 'NoBindingTeam + :> CanThrow 'NotAOneMemberTeam + :> CanThrow 'DeleteQueueFull + :> CanThrow 'TeamNotFound + :> QueryFlag "force" + :> MultiVerb1 'DELETE '[Servant.JSON] (RespondEmpty 202 "OK") + ) + :<|> Named "get-team-name" ("name" :> CanThrow 'TeamNotFound :> Get '[Servant.JSON] TeamName) + :<|> Named + "update-team-status" + ( "status" + :> CanThrow 'TeamNotFound + :> CanThrow 'InvalidTeamStatusUpdate + :> ReqBody '[Servant.JSON] TeamStatusUpdate + :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "OK") + ) + :<|> "members" + :> ( Named + "unchecked-add-team-member" + ( CanThrow 'TooManyTeamMembers + :> CanThrow 'TooManyTeamMembersOnTeamWithLegalhold + :> ReqBody '[Servant.JSON] NewTeamMember + :> MultiVerb1 'POST '[Servant.JSON] (RespondEmpty 200 "OK") + ) + :<|> Named + "unchecked-get-team-members" + ( QueryParam' '[Strict] "maxResults" (Range 1 HardTruncationLimit Int32) + :> Get '[Servant.JSON] TeamMemberList + ) + :<|> Named + "unchecked-get-team-member" + ( Capture "uid" UserId + :> CanThrow 'TeamMemberNotFound + :> Get '[Servant.JSON] TeamMember + ) + :<|> Named + "can-user-join-team" + ( "check" + :> CanThrow 'TooManyTeamMembersOnTeamWithLegalhold + :> MultiVerb1 'GET '[Servant.JSON] (RespondEmpty 200 "User can join") + ) + :<|> Named + "unchecked-update-team-member" + ( CanThrow 'AccessDenied + :> CanThrow 'InvalidPermissions + :> CanThrow 'TeamNotFound + :> CanThrow 'TeamMemberNotFound + :> CanThrow 'NotATeamMember + :> CanThrow OperationDenied + :> ReqBody '[Servant.JSON] NewTeamMember + :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "") + ) + ) + :<|> Named + "user-is-team-owner" + ( "is-team-owner" + :> Capture "uid" UserId + :> CanThrow 'AccessDenied + :> CanThrow 'TeamMemberNotFound + :> CanThrow 'NotATeamMember + :> MultiVerb1 'GET '[Servant.JSON] (RespondEmpty 200 "User is team owner") + ) + :<|> "search-visibility" + :> ( Named "get-search-visibility-internal" (Get '[Servant.JSON] TeamSearchVisibilityView) + :<|> Named + "set-search-visibility-internal" + ( CanThrow 'TeamSearchVisibilityNotEnabled + :> CanThrow OperationDenied + :> CanThrow 'NotATeamMember + :> CanThrow 'TeamNotFound + :> ReqBody '[Servant.JSON] TeamSearchVisibilityView + :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 204 "OK") + ) + ) + +type IFeatureStatusGet f = Named '("iget", f) (FeatureStatusBaseGet f) + +type IFeatureStatusPut calls errs f = Named '("iput", f) (ApplyMods calls (FeatureStatusBasePutInternal errs f)) + +type IFeatureStatusPatch calls errs f = Named '("ipatch", f) (ApplyMods calls (FeatureStatusBasePatchInternal errs f)) + +type FeatureStatusBasePutInternal errs featureConfig = + FeatureStatusBaseInternal + (AppendSymbol "Put config for " (FeatureSymbol featureConfig)) + errs + featureConfig + ( ReqBody '[Servant.JSON] (WithStatusNoLock featureConfig) + :> Put '[Servant.JSON] (WithStatus featureConfig) + ) + +type FeatureStatusBasePatchInternal errs featureConfig = + FeatureStatusBaseInternal + (AppendSymbol "Patch config for " (FeatureSymbol featureConfig)) + errs + featureConfig + ( ReqBody '[Servant.JSON] (WithStatusPatch featureConfig) + :> Patch '[Servant.JSON] (WithStatus featureConfig) + ) + +type FeatureStatusBaseInternal desc errs featureConfig a = + Summary desc + :> CanThrow OperationDenied + :> CanThrow 'NotATeamMember + :> CanThrow 'TeamNotFound + :> CanThrow TeamFeatureError + :> CanThrowMany errs + :> "teams" + :> Capture "tid" TeamId + :> "features" + :> FeatureSymbol featureConfig + :> a + +type IFeatureStatusLockStatusPut featureName = + Named + '("ilock", featureName) + ( Summary (AppendSymbol "(Un-)lock " (FeatureSymbol featureName)) + :> CanThrow 'NotATeamMember + :> CanThrow 'TeamNotFound + :> "teams" + :> Capture "tid" TeamId + :> "features" + :> FeatureSymbol featureName + :> Capture "lockStatus" LockStatus + :> Put '[Servant.JSON] LockStatusResponse + ) + +type FeatureNoConfigMultiGetBase featureName = + Summary + (AppendSymbol "Get team feature status in bulk for feature " (FeatureSymbol featureName)) + :> "features-multi-teams" + :> FeatureSymbol featureName + :> ReqBody '[Servant.JSON] TeamFeatureNoConfigMultiRequest + :> Post '[Servant.JSON] (TeamFeatureNoConfigMultiResponse featureName) + +type IFeatureNoConfigMultiGet f = + Named + '("igetmulti", f) + (FeatureNoConfigMultiGetBase f) + +swaggerDoc :: Swagger +swaggerDoc = + toSwagger (Proxy @InternalAPI) + & info . title .~ "Wire-Server internal cannon API" diff --git a/libs/galley-types/src/Galley/Types/Conversations/Intra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs similarity index 68% rename from libs/galley-types/src/Galley/Types/Conversations/Intra.hs rename to libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs index 26c941bec3..b1c07e1052 100644 --- a/libs/galley-types/src/Galley/Types/Conversations/Intra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs @@ -1,21 +1,4 @@ --- This file is part of the Wire Server implementation. --- --- Copyright (C) 2022 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.Types.Conversations.Intra +module Wire.API.Routes.Internal.Galley.ConversationsIntra ( DesiredMembership (..), Actor (..), UpsertOne2OneConversationRequest (..), @@ -28,6 +11,7 @@ import Data.Aeson.Types (FromJSON, ToJSON) import Data.Id (ConvId, UserId) import Data.Qualified import Data.Schema +import qualified Data.Swagger as Swagger import Imports data DesiredMembership = Included | Excluded @@ -62,7 +46,7 @@ data UpsertOne2OneConversationRequest = UpsertOne2OneConversationRequest uooConvId :: Maybe (Qualified ConvId) } deriving (Show, Generic) - deriving (FromJSON, ToJSON) via Schema UpsertOne2OneConversationRequest + deriving (FromJSON, ToJSON, Swagger.ToSchema) via Schema UpsertOne2OneConversationRequest instance ToSchema UpsertOne2OneConversationRequest where schema = @@ -78,7 +62,7 @@ newtype UpsertOne2OneConversationResponse = UpsertOne2OneConversationResponse { uuorConvId :: Qualified ConvId } deriving (Show, Generic) - deriving (FromJSON, ToJSON) via Schema UpsertOne2OneConversationResponse + deriving (FromJSON, ToJSON, Swagger.ToSchema) via Schema UpsertOne2OneConversationResponse instance ToSchema UpsertOne2OneConversationResponse where schema = diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs index e3b704438e..c45e17f478 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs @@ -33,7 +33,7 @@ newtype TeamFeatureNoConfigMultiRequest = TeamFeatureNoConfigMultiRequest { teams :: [TeamId] } deriving (Show, Eq) - deriving (A.ToJSON, A.FromJSON) via (Schema TeamFeatureNoConfigMultiRequest) + deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema TeamFeatureNoConfigMultiRequest) instance ToSchema TeamFeatureNoConfigMultiRequest where schema = @@ -45,7 +45,7 @@ newtype TeamFeatureNoConfigMultiResponse cfg = TeamFeatureNoConfigMultiResponse { teamsStatuses :: [TeamStatus cfg] } deriving (Show, Eq) - deriving (A.ToJSON, A.FromJSON) via (Schema (TeamFeatureNoConfigMultiResponse cfg)) + deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema (TeamFeatureNoConfigMultiResponse cfg)) instance ToSchema (TeamFeatureNoConfigMultiResponse cfg) where schema = diff --git a/libs/galley-types/src/Galley/Types/Teams/Intra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs similarity index 76% rename from libs/galley-types/src/Galley/Types/Teams/Intra.hs rename to libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs index 75369bc73a..49eb17ab0b 100644 --- a/libs/galley-types/src/Galley/Types/Teams/Intra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs @@ -1,10 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TemplateHaskell #-} -- This file is part of the Wire Server implementation. -- @@ -23,7 +19,7 @@ -- You should have received a copy of the GNU Affero General Public License along -- with this program. If not, see . -module Galley.Types.Teams.Intra +module Wire.API.Routes.Internal.Galley.TeamsIntra ( TeamStatus (..), TeamData (..), TeamStatusUpdate (..), @@ -33,7 +29,6 @@ module Galley.Types.Teams.Intra where import Data.Aeson -import Data.Aeson.TH import qualified Data.Currency as Currency import Data.Json.Util import qualified Data.Schema as S @@ -88,25 +83,25 @@ data TeamStatusUpdate = TeamStatusUpdate -- TODO: Remove Currency selection once billing supports currency changes after team creation } deriving (Eq, Show, Generic) + deriving (ToJSON, FromJSON, Swagger.ToSchema) via (S.Schema TeamStatusUpdate) -instance FromJSON TeamStatusUpdate where - parseJSON = withObject "team-status-update" $ \o -> - TeamStatusUpdate - <$> o .: "status" - <*> o .:? "currency" - -instance ToJSON TeamStatusUpdate where - toJSON s = - object - [ "status" .= tuStatus s, - "currency" .= tuCurrency s - ] +instance S.ToSchema TeamStatusUpdate where + schema = + S.object "TeamStatusUpdate" $ + TeamStatusUpdate + <$> tuStatus S..= S.field "status" S.schema + <*> tuCurrency S..= S.maybe_ (S.optField "currency" S.schema) newtype TeamName = TeamName {tnName :: Text} deriving (Eq, Show, Generic) + deriving (ToJSON, FromJSON, Swagger.ToSchema) via (S.Schema TeamName) -deriveJSON toJSONFieldName ''TeamName +instance S.ToSchema TeamName where + schema = + S.object "TeamName" $ + TeamName + <$> tnName S..= S.field "name" S.schema data GuardLegalholdPolicyConflicts = GuardLegalholdPolicyConflicts { glhProtectee :: LegalholdProtectee, @@ -114,7 +109,11 @@ data GuardLegalholdPolicyConflicts = GuardLegalholdPolicyConflicts } deriving (Show, Eq, Generic) deriving (Arbitrary) via (GenericUniform GuardLegalholdPolicyConflicts) + deriving (ToJSON, FromJSON, Swagger.ToSchema) via (S.Schema GuardLegalholdPolicyConflicts) -instance ToJSON GuardLegalholdPolicyConflicts - -instance FromJSON GuardLegalholdPolicyConflicts +instance S.ToSchema GuardLegalholdPolicyConflicts where + schema = + S.object "GuardLegalholdPolicyConflicts" $ + GuardLegalholdPolicyConflicts + <$> glhProtectee S..= S.field "glhProtectee" S.schema + <*> glhUserClients S..= S.field "glhUserClients" S.schema diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs b/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs index 530449cbfd..6a32b669cc 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs @@ -17,19 +17,30 @@ module Wire.API.Routes.Internal.LegalHold where +import Control.Lens import Data.Id +import Data.Proxy +import Data.Swagger +import Imports import Servant.API hiding (Header, WithStatus) +import Servant.Swagger +import Wire.API.SwaggerServant import Wire.API.Team.Feature type InternalLegalHoldAPI = - "i" + SwaggerTag "legalhold" + :> "i" :> "teams" - :> Capture "tid" TeamId - :> "legalhold" - :> Get '[JSON] (WithStatus LegalholdConfig) - :<|> "i" - :> "teams" - :> Capture "tid" TeamId - :> "legalhold" - :> ReqBody '[JSON] (WithStatusNoLock LegalholdConfig) - :> Put '[] NoContent + :> ( Capture "tid" TeamId + :> "legalhold" + :> Get '[JSON] (WithStatus LegalholdConfig) + :<|> Capture "tid" TeamId + :> "legalhold" + :> ReqBody '[JSON] (WithStatusNoLock LegalholdConfig) + :> Put '[] NoContent + ) + +swaggerDoc :: Swagger +swaggerDoc = + toSwagger (Proxy @InternalLegalHoldAPI) + & info . title .~ "Wire-Server internal cargohold API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs new file mode 100644 index 0000000000..967644acb3 --- /dev/null +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs @@ -0,0 +1,25 @@ +module Wire.API.Routes.Internal.Spar where + +import Control.Lens +import Data.Id +import Data.Swagger +import Imports +import Servant +import Servant.Swagger +import Wire.API.SwaggerServant +import Wire.API.User +import Wire.API.User.Saml + +type APIINTERNAL = + SwaggerTag "spar" + :> "i" + :> ( "status" :> Get '[JSON] NoContent + :<|> "teams" :> Capture "team" TeamId :> DeleteNoContent + :<|> "sso" :> "settings" :> ReqBody '[JSON] SsoSettings :> Put '[JSON] NoContent + :<|> "scim" :> "userinfos" :> ReqBody '[JSON] UserSet :> Post '[JSON] ScimUserInfos + ) + +swaggerDoc :: Swagger +swaggerDoc = + toSwagger (Proxy @APIINTERNAL) + & info . title .~ "Wire-Server internal cannon API" diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs index 6771361f3c..cc196a3b30 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs @@ -33,6 +33,7 @@ import Web.Scim.Class.Auth as Scim.Auth import Web.Scim.Class.User as Scim.User import Wire.API.Error import Wire.API.Error.Brig +import Wire.API.Routes.Internal.Spar import Wire.API.Routes.Public import Wire.API.SwaggerServant import Wire.API.User (ScimUserInfos, UserSet) @@ -49,7 +50,7 @@ type API = "sso" :> APISSO :<|> "identity-providers" :> APIIDP :<|> "scim" :> APIScim - :<|> OmitDocs :> "i" :> APIINTERNAL + :<|> OmitDocs :> APIINTERNAL type DeprecateSSOAPIV1 = Description @@ -132,12 +133,6 @@ type IdpDelete = type SsoSettingsGet = Get '[JSON] SsoSettings -type APIINTERNAL = - "status" :> Get '[JSON] NoContent - :<|> "teams" :> Capture "team" TeamId :> DeleteNoContent - :<|> "sso" :> "settings" :> ReqBody '[JSON] SsoSettings :> Put '[JSON] NoContent - :<|> "scim" :> "userinfos" :> ReqBody '[JSON] UserSet :> Post '[JSON] ScimUserInfos - sparSPIssuer :: (Functor m, SAML.HasConfig m) => Maybe TeamId -> m SAML.Issuer sparSPIssuer Nothing = SAML.Issuer <$> SAML.getSsoURI (Proxy @APISSO) (Proxy @APIAuthRespLegacy) diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index eb5b461fc5..31264d3985 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -141,7 +141,7 @@ import Wire.Arbitrary (Arbitrary, GenericUniform (..)) -- 'FeatureStatusPut' (optional) and by by user: 'FeatureConfigGet'. Then -- implement them in Galley.API.Public.Feature. -- --- 7. Add internal routes in Galley.API.Internal +-- 7. Add internal routes in Wire.API.Routes.Internal.Galley -- -- 8. If the feature should be configurable via Stern add routes to Stern.API. -- Manually check that the swagger looks okay and works. @@ -469,6 +469,9 @@ instance ToSchema LockStatus where element "unlocked" LockStatusUnlocked ] +-- TODO: Check if this generic instantiation is/looks good +instance S.ToParamSchema LockStatus + instance ToByteString LockStatus where builder LockStatusLocked = "locked" builder LockStatusUnlocked = "unlocked" @@ -689,6 +692,7 @@ instance FeatureTrivialConfig SndFactorPasswordChallengeConfig where data SearchVisibilityInboundConfig = SearchVisibilityInboundConfig deriving stock (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform SearchVisibilityInboundConfig) + deriving (S.ToSchema) via Schema SearchVisibilityInboundConfig instance IsFeatureConfig SearchVisibilityInboundConfig where type FeatureSymbol SearchVisibilityInboundConfig = "searchVisibilityInbound" diff --git a/libs/wire-api/src/Wire/API/Team/LegalHold.hs b/libs/wire-api/src/Wire/API/Team/LegalHold.hs index d4f93a235a..69c97b0d9e 100644 --- a/libs/wire-api/src/Wire/API/Team/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Team/LegalHold.hs @@ -1,4 +1,5 @@ {-# LANGUAGE StrictData #-} +{-# LANGUAGE TemplateHaskell #-} -- This file is part of the Wire Server implementation. -- @@ -29,6 +30,7 @@ module Wire.API.Team.LegalHold ) where +import Control.Lens hiding (element, enum, (.=)) import qualified Data.Aeson.Types as A import Data.Id import Data.LegalHold @@ -208,6 +210,12 @@ instance ToSchema ApproveLegalHoldForUserRequest where ----------------------------------------------------------------------- +data LegalholdProtecteeTag + = ProtectedUserTag + | UnprotectedBotTag + | LegalholdPlusFederationNotImplementedTag + deriving (Eq, Enum, Bounded) + -- | Bots are not protected to be potentially recorded by legalhold devices. data LegalholdProtectee = ProtectedUser UserId @@ -220,9 +228,45 @@ data LegalholdProtectee deriving (Show, Eq, Ord, Generic) deriving (Arbitrary) via (GenericUniform LegalholdProtectee) -instance ToJSON LegalholdProtectee +$(makePrisms ''LegalholdProtectee) -- {"tag":"ProtectedUser","contents":"110a187a-be5b-11eb-8f47-370bc8e40f35"} -- {"tag":"UnprotectedBot"} -- {"tag":"LegalholdPlusFederationNotImplemented"} -instance FromJSON LegalholdProtectee +instance ToSchema LegalholdProtectee where + schema :: ValueSchema NamedSwaggerDoc LegalholdProtectee + schema = + object "LegalholdProtectee" $ + fromTagged + <$> toTagged + .= bind + (fst .= field "tag" tagSchema) + (snd .= fieldOver _1 "value" untaggedSchema) + where + toTagged :: LegalholdProtectee -> (LegalholdProtecteeTag, LegalholdProtectee) + toTagged d@(ProtectedUser _) = (ProtectedUserTag, d) + toTagged d@UnprotectedBot = (UnprotectedBotTag, d) + toTagged d@LegalholdPlusFederationNotImplemented = (LegalholdPlusFederationNotImplementedTag, d) + + fromTagged :: (LegalholdProtecteeTag, LegalholdProtectee) -> LegalholdProtectee + fromTagged = snd + + untaggedSchema = dispatch $ \case + ProtectedUserTag -> tag _ProtectedUser (unnamed schema) + UnprotectedBotTag -> tag _UnprotectedBot null_ + LegalholdPlusFederationNotImplementedTag -> tag _LegalholdPlusFederationNotImplemented null_ + + tagSchema :: ValueSchema NamedSwaggerDoc LegalholdProtecteeTag + tagSchema = + enum @Text "LegalholdProtecteeTag" $ + mconcat + [ element "ProtectedUser" ProtectedUserTag, + element "UnprotectedBot" UnprotectedBotTag, + element "LegalholdPlusFederationNotImplemented" LegalholdPlusFederationNotImplementedTag + ] + +deriving via (Schema LegalholdProtectee) instance (ToJSON LegalholdProtectee) + +deriving via (Schema LegalholdProtectee) instance (FromJSON LegalholdProtectee) + +deriving via (Schema LegalholdProtectee) instance (S.ToSchema LegalholdProtectee) diff --git a/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs b/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs index 9f0bd912dd..304bc7603a 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs @@ -46,6 +46,7 @@ import qualified Wire.API.Provider.External as Provider.External import qualified Wire.API.Provider.Service as Provider.Service import qualified Wire.API.Provider.Service.Tag as Provider.Service.Tag import qualified Wire.API.Push.Token as Push.Token +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as TeamsIntra import qualified Wire.API.Team as Team import qualified Wire.API.Team.Conversation as Team.Conversation import qualified Wire.API.Team.Feature as Team.Feature @@ -310,7 +311,8 @@ tests = testRoundTrip @User.Search.PagingState, testRoundTrip @User.Search.TeamContact, testRoundTrip @(Wrapped.Wrapped "some_int" Int), - testRoundTrip @Conversation.Action.SomeConversationAction + testRoundTrip @Conversation.Action.SomeConversationAction, + testRoundTrip @TeamsIntra.GuardLegalholdPolicyConflicts ] testRoundTrip :: diff --git a/libs/wire-api/wire-api.cabal b/libs/wire-api/wire-api.cabal index ab98c5062c..3be0437bb4 100644 --- a/libs/wire-api/wire-api.cabal +++ b/libs/wire-api/wire-api.cabal @@ -82,8 +82,12 @@ library Wire.API.Routes.Internal.Brig.EJPD Wire.API.Routes.Internal.Cannon Wire.API.Routes.Internal.Cargohold + Wire.API.Routes.Internal.Galley + Wire.API.Routes.Internal.Galley.ConversationsIntra Wire.API.Routes.Internal.Galley.TeamFeatureNoConfigMulti + Wire.API.Routes.Internal.Galley.TeamsIntra Wire.API.Routes.Internal.LegalHold + Wire.API.Routes.Internal.Spar Wire.API.Routes.LowLevelStream Wire.API.Routes.MultiTablePaging Wire.API.Routes.MultiTablePaging.State diff --git a/services/brig/src/Brig/API/Connection/Remote.hs b/services/brig/src/Brig/API/Connection/Remote.hs index 4567753e68..0138d049ca 100644 --- a/services/brig/src/Brig/API/Connection/Remote.hs +++ b/services/brig/src/Brig/API/Connection/Remote.hs @@ -35,7 +35,6 @@ import Control.Error.Util ((??)) import Control.Monad.Trans.Except (runExceptT, throwE) import Data.Id as Id import Data.Qualified -import Galley.Types.Conversations.Intra (Actor (..), DesiredMembership (..), UpsertOne2OneConversationRequest (..), UpsertOne2OneConversationResponse (uuorConvId)) import Imports import Network.Wai.Utilities.Error import Wire.API.Connection @@ -44,6 +43,7 @@ import Wire.API.Federation.API.Brig ( NewConnectionResponse (..), RemoteConnectionAction (..), ) +import Wire.API.Routes.Internal.Galley.ConversationsIntra (Actor (..), DesiredMembership (..), UpsertOne2OneConversationRequest (..), UpsertOne2OneConversationResponse (uuorConvId)) import Wire.API.Routes.Public.Util (ResponseForExistedCreated (..)) data LocalConnectionAction diff --git a/services/brig/src/Brig/API/Public.hs b/services/brig/src/Brig/API/Public.hs index 3d856b4c78..e709e615b0 100644 --- a/services/brig/src/Brig/API/Public.hs +++ b/services/brig/src/Brig/API/Public.hs @@ -109,7 +109,11 @@ import qualified Wire.API.Error.Brig as E import Wire.API.Federation.API import qualified Wire.API.Properties as Public import qualified Wire.API.Routes.Internal.Brig as BrigInternalAPI -import Wire.API.Routes.Internal.Cannon as CannonInternalAPI +import qualified Wire.API.Routes.Internal.Cannon as CannonInternalAPI +import qualified Wire.API.Routes.Internal.Cargohold as CargoholdInternalAPI +import qualified Wire.API.Routes.Internal.Galley as GalleyInternalAPI +import qualified Wire.API.Routes.Internal.LegalHold as LegalHoldInternalAPI +import qualified Wire.API.Routes.Internal.Spar as SparInternalAPI import qualified Wire.API.Routes.MultiTablePaging as Public import Wire.API.Routes.Named (Named (Named)) import Wire.API.Routes.Public.Brig @@ -179,6 +183,10 @@ internalEndpointsSwaggerDocsAPI (Just V3) = swaggerSchemaUIServer $ ( BrigInternalAPI.swaggerDoc <> CannonInternalAPI.swaggerDoc + <> CargoholdInternalAPI.swaggerDoc + <> LegalHoldInternalAPI.swaggerDoc + <> GalleyInternalAPI.swaggerDoc + <> SparInternalAPI.swaggerDoc ) & S.info . S.title .~ "Wire-Server internal API" & S.info . S.description ?~ $(embedText =<< makeRelativeToProject "docs/swagger-internal-endpoints.md") diff --git a/services/brig/src/Brig/API/User.hs b/services/brig/src/Brig/API/User.hs index fb93e2e4f0..6cef15d026 100644 --- a/services/brig/src/Brig/API/User.hs +++ b/services/brig/src/Brig/API/User.hs @@ -161,7 +161,6 @@ import Data.Qualified import Data.Time.Clock (addUTCTime, diffUTCTime) import Data.UUID.V4 (nextRandom) import qualified Galley.Types.Teams as Team -import qualified Galley.Types.Teams.Intra as Team import Imports import Network.Wai.Utilities import Polysemy @@ -175,6 +174,7 @@ import qualified Wire.API.Error.Brig as E import Wire.API.Federation.API import Wire.API.Federation.Error import Wire.API.Routes.Internal.Brig.Connection +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Team hiding (newTeam) import Wire.API.Team.Feature (forgetLock) import Wire.API.Team.Invitation diff --git a/services/brig/src/Brig/Effects/GalleyProvider.hs b/services/brig/src/Brig/Effects/GalleyProvider.hs index afbe6b5875..1ef19c495d 100644 --- a/services/brig/src/Brig/Effects/GalleyProvider.hs +++ b/services/brig/src/Brig/Effects/GalleyProvider.hs @@ -8,11 +8,11 @@ import qualified Data.Currency as Currency import Data.Id import Data.Json.Util (UTCTimeMillis) import Data.Qualified -import qualified Galley.Types.Teams.Intra as Team import Imports import qualified Network.Wai.Utilities.Error as Wai import Polysemy import Wire.API.Conversation +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Team import qualified Wire.API.Team.Conversation as Conv import Wire.API.Team.Feature diff --git a/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs b/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs index a3cb6c2e37..d7e3f96825 100644 --- a/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs +++ b/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs @@ -21,7 +21,6 @@ import Data.Json.Util (UTCTimeMillis) import Data.Qualified import Data.Range import qualified Galley.Types.Teams as Team -import qualified Galley.Types.Teams.Intra as Team import Imports import Network.HTTP.Types.Method import Network.HTTP.Types.Status @@ -31,6 +30,7 @@ import Polysemy.Error import Servant.API (toHeader) import System.Logger (Msg, field, msg, val) import Wire.API.Conversation hiding (Member) +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Routes.Version import Wire.API.Team import qualified Wire.API.Team.Conversation as Conv diff --git a/services/brig/src/Brig/IO/Intra.hs b/services/brig/src/Brig/IO/Intra.hs index 966c7f6430..72edb33fcc 100644 --- a/services/brig/src/Brig/IO/Intra.hs +++ b/services/brig/src/Brig/IO/Intra.hs @@ -86,8 +86,6 @@ import Data.Qualified import Data.Range import qualified Data.Set as Set import GHC.TypeLits -import Galley.Types.Conversations.Intra (UpsertOne2OneConversationRequest, UpsertOne2OneConversationResponse) -import Galley.Types.Teams.Intra (GuardLegalholdPolicyConflicts (GuardLegalholdPolicyConflicts)) import Gundeck.Types.Push.V2 import qualified Gundeck.Types.Push.V2 as Push import Imports @@ -102,6 +100,8 @@ import Wire.API.Federation.API import Wire.API.Federation.API.Brig import Wire.API.Federation.Error import Wire.API.Properties +import Wire.API.Routes.Internal.Galley.ConversationsIntra (UpsertOne2OneConversationRequest, UpsertOne2OneConversationResponse) +import Wire.API.Routes.Internal.Galley.TeamsIntra (GuardLegalholdPolicyConflicts (GuardLegalholdPolicyConflicts)) import Wire.API.Team.LegalHold (LegalholdProtectee) import qualified Wire.API.Team.Member as Team import Wire.API.User diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index c8e2776bd3..6578214d9a 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -53,7 +53,6 @@ import qualified Data.List1 as List1 import Data.Range import Data.String.Conversions (cs) import qualified Galley.Types.Teams as Team -import qualified Galley.Types.Teams.Intra as Team import Imports hiding (head) import Network.HTTP.Types.Status import Network.Wai (Response) @@ -68,6 +67,7 @@ import Util.Logging (logFunction, logTeam) import Wire.API.Error import qualified Wire.API.Error.Brig as E import Wire.API.Federation.API +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Routes.Named import Wire.API.Routes.Public.Brig import Wire.API.Team diff --git a/services/brig/test/integration/API/Team.hs b/services/brig/test/integration/API/Team.hs index 2ec5c01455..4bea265310 100644 --- a/services/brig/test/integration/API/Team.hs +++ b/services/brig/test/integration/API/Team.hs @@ -47,7 +47,6 @@ import Data.Time (addUTCTime, getCurrentTime) import qualified Data.UUID as UUID (fromString) import qualified Data.UUID.V4 as UUID import qualified Galley.Types.Teams as Team -import qualified Galley.Types.Teams.Intra as Team import Imports import qualified Network.HTTP.Types as HTTP import qualified Network.Wai as Wai @@ -64,6 +63,7 @@ import Util.AWS as Util import Web.Cookie (parseSetCookie, setCookieName) import Wire.API.Asset import Wire.API.Connection +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Team hiding (newTeam) import Wire.API.Team.Feature import qualified Wire.API.Team.Feature as Public diff --git a/services/brig/test/integration/API/Team/Util.hs b/services/brig/test/integration/API/Team/Util.hs index f1ebd61657..cec99bf0b3 100644 --- a/services/brig/test/integration/API/Team/Util.hs +++ b/services/brig/test/integration/API/Team/Util.hs @@ -34,7 +34,6 @@ import Data.Misc (Milliseconds) import Data.Range import qualified Data.Set as Set import qualified Data.Text.Encoding as T -import qualified Galley.Types.Teams.Intra as Team import Imports import qualified Network.Wai.Utilities.Error as Error import Test.Tasty.HUnit @@ -43,6 +42,7 @@ import Web.Cookie (parseSetCookie, setCookieName) import Wire.API.Conversation import Wire.API.Conversation.Protocol import Wire.API.Conversation.Role +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Team hiding (newTeam) import Wire.API.Team.Feature (FeatureStatus (..)) import qualified Wire.API.Team.Feature as Public diff --git a/services/galley/src/Galley/API/Internal.hs b/services/galley/src/Galley/API/Internal.hs index c334e60031..dc9107dcf5 100644 --- a/services/galley/src/Galley/API/Internal.hs +++ b/services/galley/src/Galley/API/Internal.hs @@ -33,7 +33,6 @@ import Data.Range import Data.Singletons import Data.String.Conversions (cs) import Data.Time -import GHC.TypeLits (AppendSymbol) import qualified Galley.API.Clients as Clients import qualified Galley.API.Create as Create import qualified Galley.API.CustomBackend as CustomBackend @@ -68,9 +67,7 @@ import Galley.Options import qualified Galley.Queue as Q import Galley.Types.Bot (AddBot, RemoveBot) import Galley.Types.Bot.Service -import Galley.Types.Conversations.Intra (UpsertOne2OneConversationRequest (..), UpsertOne2OneConversationResponse (..)) import Galley.Types.Conversations.Members (RemoteMember (rmId)) -import Galley.Types.Teams.Intra import Galley.Types.UserList import Imports hiding (head) import Network.Wai.Predicate hiding (Error, err) @@ -83,13 +80,10 @@ import Polysemy.Error import Polysemy.Input import qualified Polysemy.TinyLog as P import Servant hiding (JSON, WithStatus) -import qualified Servant hiding (WithStatus) import System.Logger.Class hiding (Path, name) import qualified System.Logger.Class as Log -import Wire.API.ApplyMods import Wire.API.Conversation hiding (Member) import Wire.API.Conversation.Action -import Wire.API.Conversation.Role import Wire.API.CustomBackend import Wire.API.Error import Wire.API.Error.Galley @@ -99,380 +93,14 @@ import Wire.API.Federation.API.Galley import Wire.API.Federation.Error import Wire.API.Provider.Service hiding (Service) import Wire.API.Routes.API -import Wire.API.Routes.Internal.Galley.TeamFeatureNoConfigMulti +import Wire.API.Routes.Internal.Galley +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Routes.MultiTablePaging (mtpHasMore, mtpPagingState, mtpResults) -import Wire.API.Routes.MultiVerb -import Wire.API.Routes.Named -import Wire.API.Routes.Public -import Wire.API.Routes.Public.Galley.Conversation -import Wire.API.Routes.Public.Galley.Feature -import Wire.API.Team import Wire.API.Team.Feature import Wire.API.Team.Member -import Wire.API.Team.SearchVisibility import Wire.Sem.Paging import Wire.Sem.Paging.Cassandra -type LegalHoldFeatureStatusChangeErrors = - '( 'ActionDenied 'RemoveConversationMember, - '( AuthenticationError, - '( 'CannotEnableLegalHoldServiceLargeTeam, - '( 'LegalHoldNotEnabled, - '( 'LegalHoldDisableUnimplemented, - '( 'LegalHoldServiceNotRegistered, - '( 'UserLegalHoldIllegalOperation, - '( 'LegalHoldCouldNotBlockConnections, '()) - ) - ) - ) - ) - ) - ) - ) - -type LegalHoldFeaturesStatusChangeFederatedCalls = - '[ MakesFederatedCall 'Galley "on-conversation-updated", - MakesFederatedCall 'Galley "on-mls-message-sent", - MakesFederatedCall 'Galley "on-new-remote-conversation" - ] - -type IFeatureAPI = - -- SSOConfig - IFeatureStatusGet SSOConfig - :<|> IFeatureStatusPut '[] '() SSOConfig - :<|> IFeatureStatusPatch '[] '() SSOConfig - -- LegalholdConfig - :<|> IFeatureStatusGet LegalholdConfig - :<|> IFeatureStatusPut - LegalHoldFeaturesStatusChangeFederatedCalls - LegalHoldFeatureStatusChangeErrors - LegalholdConfig - :<|> IFeatureStatusPatch - LegalHoldFeaturesStatusChangeFederatedCalls - LegalHoldFeatureStatusChangeErrors - LegalholdConfig - -- SearchVisibilityAvailableConfig - :<|> IFeatureStatusGet SearchVisibilityAvailableConfig - :<|> IFeatureStatusPut '[] '() SearchVisibilityAvailableConfig - :<|> IFeatureStatusPatch '[] '() SearchVisibilityAvailableConfig - -- ValidateSAMLEmailsConfig - :<|> IFeatureStatusGet ValidateSAMLEmailsConfig - :<|> IFeatureStatusPut '[] '() ValidateSAMLEmailsConfig - :<|> IFeatureStatusPatch '[] '() ValidateSAMLEmailsConfig - -- DigitalSignaturesConfig - :<|> IFeatureStatusGet DigitalSignaturesConfig - :<|> IFeatureStatusPut '[] '() DigitalSignaturesConfig - :<|> IFeatureStatusPatch '[] '() DigitalSignaturesConfig - -- AppLockConfig - :<|> IFeatureStatusGet AppLockConfig - :<|> IFeatureStatusPut '[] '() AppLockConfig - :<|> IFeatureStatusPatch '[] '() AppLockConfig - -- FileSharingConfig - :<|> IFeatureStatusGet FileSharingConfig - :<|> IFeatureStatusPut '[] '() FileSharingConfig - :<|> IFeatureStatusLockStatusPut FileSharingConfig - :<|> IFeatureStatusPatch '[] '() FileSharingConfig - -- ConferenceCallingConfig - :<|> IFeatureStatusGet ConferenceCallingConfig - :<|> IFeatureStatusPut '[] '() ConferenceCallingConfig - :<|> IFeatureStatusPatch '[] '() ConferenceCallingConfig - -- SelfDeletingMessagesConfig - :<|> IFeatureStatusGet SelfDeletingMessagesConfig - :<|> IFeatureStatusPut '[] '() SelfDeletingMessagesConfig - :<|> IFeatureStatusLockStatusPut SelfDeletingMessagesConfig - :<|> IFeatureStatusPatch '[] '() SelfDeletingMessagesConfig - -- GuestLinksConfig - :<|> IFeatureStatusGet GuestLinksConfig - :<|> IFeatureStatusPut '[] '() GuestLinksConfig - :<|> IFeatureStatusLockStatusPut GuestLinksConfig - :<|> IFeatureStatusPatch '[] '() GuestLinksConfig - -- SndFactorPasswordChallengeConfig - :<|> IFeatureStatusGet SndFactorPasswordChallengeConfig - :<|> IFeatureStatusPut '[] '() SndFactorPasswordChallengeConfig - :<|> IFeatureStatusLockStatusPut SndFactorPasswordChallengeConfig - :<|> IFeatureStatusPatch '[] '() SndFactorPasswordChallengeConfig - -- SearchVisibilityInboundConfig - :<|> IFeatureStatusGet SearchVisibilityInboundConfig - :<|> IFeatureStatusPut '[] '() SearchVisibilityInboundConfig - :<|> IFeatureStatusPatch '[] '() SearchVisibilityInboundConfig - :<|> IFeatureNoConfigMultiGet SearchVisibilityInboundConfig - -- ClassifiedDomainsConfig - :<|> IFeatureStatusGet ClassifiedDomainsConfig - -- MLSConfig - :<|> IFeatureStatusGet MLSConfig - :<|> IFeatureStatusPut '[] '() MLSConfig - :<|> IFeatureStatusPatch '[] '() MLSConfig - -- ExposeInvitationURLsToTeamAdminConfig - :<|> IFeatureStatusGet ExposeInvitationURLsToTeamAdminConfig - :<|> IFeatureStatusPut '[] '() ExposeInvitationURLsToTeamAdminConfig - :<|> IFeatureStatusPatch '[] '() ExposeInvitationURLsToTeamAdminConfig - -- SearchVisibilityInboundConfig - :<|> IFeatureStatusGet SearchVisibilityInboundConfig - :<|> IFeatureStatusPut '[] '() SearchVisibilityInboundConfig - :<|> IFeatureStatusPatch '[] '() SearchVisibilityInboundConfig - -- OutlookCalIntegrationConfig - :<|> IFeatureStatusGet OutlookCalIntegrationConfig - :<|> IFeatureStatusPut '[] '() OutlookCalIntegrationConfig - :<|> IFeatureStatusPatch '[] '() OutlookCalIntegrationConfig - :<|> IFeatureStatusLockStatusPut OutlookCalIntegrationConfig - -- all feature configs - :<|> Named - "feature-configs-internal" - ( Summary "Get all feature configs (for user/team; if n/a fall back to site config)." - :> "feature-configs" - :> CanThrow OperationDenied - :> CanThrow 'NotATeamMember - :> CanThrow 'TeamNotFound - :> QueryParam' - [ Optional, - Strict, - Description "Optional user id" - ] - "user_id" - UserId - :> Get '[Servant.JSON] AllFeatureConfigs - ) - -type InternalAPI = "i" :> InternalAPIBase - -type InternalAPIBase = - Named - "status" - ( "status" :> MultiVerb 'GET '[Servant.JSON] '[RespondEmpty 200 "OK"] () - ) - -- This endpoint can lead to the following events being sent: - -- - MemberLeave event to members for all conversations the user was in - :<|> Named - "delete-user" - ( Summary - "Remove a user from their teams and conversations and erase their clients" - :> MakesFederatedCall 'Galley "on-conversation-updated" - :> MakesFederatedCall 'Galley "on-user-deleted-conversations" - :> MakesFederatedCall 'Galley "on-mls-message-sent" - :> ZLocalUser - :> ZOptConn - :> "user" - :> MultiVerb 'DELETE '[Servant.JSON] '[RespondEmpty 200 "Remove a user from Galley"] () - ) - -- This endpoint can lead to the following events being sent: - -- - ConvCreate event to self, if conversation did not exist before - -- - ConvConnect event to self, if other didn't join the connect conversation before - :<|> Named - "connect" - ( Summary "Create a connect conversation (deprecated)" - :> MakesFederatedCall 'Galley "on-conversation-created" - :> CanThrow 'ConvNotFound - :> CanThrow 'InvalidOperation - :> CanThrow 'NotConnected - :> ZLocalUser - :> ZOptConn - :> "conversations" - :> "connect" - :> ReqBody '[Servant.JSON] Connect - :> ConversationVerb - ) - :<|> Named - "guard-legalhold-policy-conflicts" - ( "guard-legalhold-policy-conflicts" - :> CanThrow 'MissingLegalholdConsent - :> ReqBody '[Servant.JSON] GuardLegalholdPolicyConflicts - :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "Guard Legalhold Policy") - ) - :<|> ILegalholdWhitelistedTeamsAPI - :<|> ITeamsAPI - :<|> Named - "upsert-one2one" - ( Summary "Create or Update a connect or one2one conversation." - :> "conversations" - :> "one2one" - :> "upsert" - :> ReqBody '[Servant.JSON] UpsertOne2OneConversationRequest - :> Post '[Servant.JSON] UpsertOne2OneConversationResponse - ) - :<|> IFeatureAPI - -type ILegalholdWhitelistedTeamsAPI = - "legalhold" - :> "whitelisted-teams" - :> Capture "tid" TeamId - :> ILegalholdWhitelistedTeamsAPIBase - -type ILegalholdWhitelistedTeamsAPIBase = - Named - "set-team-legalhold-whitelisted" - (MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "Team Legalhold Whitelisted")) - :<|> Named - "unset-team-legalhold-whitelisted" - (MultiVerb1 'DELETE '[Servant.JSON] (RespondEmpty 204 "Team Legalhold un-Whitelisted")) - :<|> Named - "get-team-legalhold-whitelisted" - ( MultiVerb - 'GET - '[Servant.JSON] - '[ RespondEmpty 404 "Team not Legalhold Whitelisted", - RespondEmpty 200 "Team Legalhold Whitelisted" - ] - Bool - ) - -type ITeamsAPI = "teams" :> Capture "tid" TeamId :> ITeamsAPIBase - -type ITeamsAPIBase = - Named "get-team-internal" (CanThrow 'TeamNotFound :> Get '[Servant.JSON] TeamData) - :<|> Named - "create-binding-team" - ( ZUser - :> ReqBody '[Servant.JSON] BindingNewTeam - :> MultiVerb1 - 'PUT - '[Servant.JSON] - ( WithHeaders - '[Header "Location" TeamId] - TeamId - (RespondEmpty 201 "OK") - ) - ) - :<|> Named - "delete-binding-team" - ( CanThrow 'NoBindingTeam - :> CanThrow 'NotAOneMemberTeam - :> CanThrow 'DeleteQueueFull - :> CanThrow 'TeamNotFound - :> QueryFlag "force" - :> MultiVerb1 'DELETE '[Servant.JSON] (RespondEmpty 202 "OK") - ) - :<|> Named "get-team-name" ("name" :> CanThrow 'TeamNotFound :> Get '[Servant.JSON] TeamName) - :<|> Named - "update-team-status" - ( "status" - :> CanThrow 'TeamNotFound - :> CanThrow 'InvalidTeamStatusUpdate - :> ReqBody '[Servant.JSON] TeamStatusUpdate - :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "OK") - ) - :<|> "members" - :> ( Named - "unchecked-add-team-member" - ( CanThrow 'TooManyTeamMembers - :> CanThrow 'TooManyTeamMembersOnTeamWithLegalhold - :> ReqBody '[Servant.JSON] NewTeamMember - :> MultiVerb1 'POST '[Servant.JSON] (RespondEmpty 200 "OK") - ) - :<|> Named - "unchecked-get-team-members" - ( QueryParam' '[Strict] "maxResults" (Range 1 HardTruncationLimit Int32) - :> Get '[Servant.JSON] TeamMemberList - ) - :<|> Named - "unchecked-get-team-member" - ( Capture "uid" UserId - :> CanThrow 'TeamMemberNotFound - :> Get '[Servant.JSON] TeamMember - ) - :<|> Named - "can-user-join-team" - ( "check" - :> CanThrow 'TooManyTeamMembersOnTeamWithLegalhold - :> MultiVerb1 'GET '[Servant.JSON] (RespondEmpty 200 "User can join") - ) - :<|> Named - "unchecked-update-team-member" - ( CanThrow 'AccessDenied - :> CanThrow 'InvalidPermissions - :> CanThrow 'TeamNotFound - :> CanThrow 'TeamMemberNotFound - :> CanThrow 'NotATeamMember - :> CanThrow OperationDenied - :> ReqBody '[Servant.JSON] NewTeamMember - :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 200 "") - ) - ) - :<|> Named - "user-is-team-owner" - ( "is-team-owner" - :> Capture "uid" UserId - :> CanThrow 'AccessDenied - :> CanThrow 'TeamMemberNotFound - :> CanThrow 'NotATeamMember - :> MultiVerb1 'GET '[Servant.JSON] (RespondEmpty 200 "User is team owner") - ) - :<|> "search-visibility" - :> ( Named "get-search-visibility-internal" (Get '[Servant.JSON] TeamSearchVisibilityView) - :<|> Named - "set-search-visibility-internal" - ( CanThrow 'TeamSearchVisibilityNotEnabled - :> CanThrow OperationDenied - :> CanThrow 'NotATeamMember - :> CanThrow 'TeamNotFound - :> ReqBody '[Servant.JSON] TeamSearchVisibilityView - :> MultiVerb1 'PUT '[Servant.JSON] (RespondEmpty 204 "OK") - ) - ) - -type IFeatureStatusGet f = Named '("iget", f) (FeatureStatusBaseGet f) - -type IFeatureStatusPut calls errs f = Named '("iput", f) (ApplyMods calls (FeatureStatusBasePutInternal errs f)) - -type IFeatureStatusPatch calls errs f = Named '("ipatch", f) (ApplyMods calls (FeatureStatusBasePatchInternal errs f)) - -type FeatureStatusBasePutInternal errs featureConfig = - FeatureStatusBaseInternal - (AppendSymbol "Put config for " (FeatureSymbol featureConfig)) - errs - featureConfig - ( ReqBody '[Servant.JSON] (WithStatusNoLock featureConfig) - :> Put '[Servant.JSON] (WithStatus featureConfig) - ) - -type FeatureStatusBasePatchInternal errs featureConfig = - FeatureStatusBaseInternal - (AppendSymbol "Patch config for " (FeatureSymbol featureConfig)) - errs - featureConfig - ( ReqBody '[Servant.JSON] (WithStatusPatch featureConfig) - :> Patch '[Servant.JSON] (WithStatus featureConfig) - ) - -type FeatureStatusBaseInternal desc errs featureConfig a = - Summary desc - :> CanThrow OperationDenied - :> CanThrow 'NotATeamMember - :> CanThrow 'TeamNotFound - :> CanThrow TeamFeatureError - :> CanThrowMany errs - :> "teams" - :> Capture "tid" TeamId - :> "features" - :> FeatureSymbol featureConfig - :> a - -type IFeatureStatusLockStatusPut featureName = - Named - '("ilock", featureName) - ( Summary (AppendSymbol "(Un-)lock " (FeatureSymbol featureName)) - :> CanThrow 'NotATeamMember - :> CanThrow 'TeamNotFound - :> "teams" - :> Capture "tid" TeamId - :> "features" - :> FeatureSymbol featureName - :> Capture "lockStatus" LockStatus - :> Put '[Servant.JSON] LockStatusResponse - ) - -type FeatureNoConfigMultiGetBase featureName = - Summary - (AppendSymbol "Get team feature status in bulk for feature " (FeatureSymbol featureName)) - :> "features-multi-teams" - :> FeatureSymbol featureName - :> ReqBody '[Servant.JSON] TeamFeatureNoConfigMultiRequest - :> Post '[Servant.JSON] (TeamFeatureNoConfigMultiResponse featureName) - -type IFeatureNoConfigMultiGet f = - Named - '("igetmulti", f) - (FeatureNoConfigMultiGetBase f) - internalAPI :: API InternalAPI GalleyEffects internalAPI = hoistAPI @InternalAPIBase id $ diff --git a/services/galley/src/Galley/API/One2One.hs b/services/galley/src/Galley/API/One2One.hs index dfdc2aec68..0a8400d862 100644 --- a/services/galley/src/Galley/API/One2One.hs +++ b/services/galley/src/Galley/API/One2One.hs @@ -29,7 +29,6 @@ import Galley.Data.Conversation import Galley.Data.Conversation.Types import Galley.Effects.ConversationStore import Galley.Effects.MemberStore -import Galley.Types.Conversations.Intra (Actor (..), DesiredMembership (..), UpsertOne2OneConversationRequest (..), UpsertOne2OneConversationResponse (..)) import Galley.Types.Conversations.One2One (one2OneConvId) import Galley.Types.ToUserRole import Galley.Types.UserList @@ -37,6 +36,7 @@ import Imports import Polysemy import Wire.API.Conversation import Wire.API.Conversation.Protocol +import Wire.API.Routes.Internal.Galley.ConversationsIntra (Actor (..), DesiredMembership (..), UpsertOne2OneConversationRequest (..), UpsertOne2OneConversationResponse (..)) newConnectConversationWithRemote :: Local UserId -> diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index efab3a93c1..3d42884228 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -109,7 +109,6 @@ import Galley.Intra.Push import Galley.Options import qualified Galley.Types.Conversations.Members as Conv import Galley.Types.Teams -import Galley.Types.Teams.Intra import Galley.Types.UserList import Imports hiding (forkIO) import Network.Wai @@ -131,6 +130,7 @@ import Wire.API.Event.Team import Wire.API.Federation.API import Wire.API.Federation.Error import qualified Wire.API.Message as Conv +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Routes.MultiTablePaging (MultiTablePage (MultiTablePage), MultiTablePagingState (mtpsState)) import Wire.API.Routes.Public.Galley.TeamMember import Wire.API.Team diff --git a/services/galley/src/Galley/Cassandra/Instances.hs b/services/galley/src/Galley/Cassandra/Instances.hs index 90b648e8ff..24d4e8a87a 100644 --- a/services/galley/src/Galley/Cassandra/Instances.hs +++ b/services/galley/src/Galley/Cassandra/Instances.hs @@ -32,7 +32,6 @@ import Data.Either.Combinators hiding (fromRight) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Galley.Types.Bot () -import Galley.Types.Teams.Intra import Imports import Wire.API.Asset (AssetKey, assetKeyToText) import Wire.API.Conversation @@ -41,6 +40,7 @@ import Wire.API.MLS.CipherSuite import Wire.API.MLS.Proposal import Wire.API.MLS.PublicGroupState import Wire.API.MLS.Serialisation +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Team import qualified Wire.API.Team.Feature as Public import Wire.API.Team.SearchVisibility diff --git a/services/galley/src/Galley/Cassandra/Queries.hs b/services/galley/src/Galley/Cassandra/Queries.hs index 5cdac44f74..6796456070 100644 --- a/services/galley/src/Galley/Cassandra/Queries.hs +++ b/services/galley/src/Galley/Cassandra/Queries.hs @@ -27,7 +27,6 @@ import Data.Misc import qualified Data.Text.Lazy as LT import Galley.Cassandra.Instances () import Galley.Data.Scope -import Galley.Types.Teams.Intra import Imports import Text.RawString.QQ import Wire.API.Conversation @@ -39,6 +38,7 @@ import Wire.API.MLS.KeyPackage import Wire.API.MLS.PublicGroupState import Wire.API.Provider import Wire.API.Provider.Service +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Team import Wire.API.Team.Permission import Wire.API.Team.SearchVisibility diff --git a/services/galley/src/Galley/Cassandra/Team.hs b/services/galley/src/Galley/Cassandra/Team.hs index ff03dc0062..a2eed6014d 100644 --- a/services/galley/src/Galley/Cassandra/Team.hs +++ b/services/galley/src/Galley/Cassandra/Team.hs @@ -51,11 +51,11 @@ import Galley.Env import Galley.Monad import Galley.Options import Galley.Types.Teams -import Galley.Types.Teams.Intra import Imports hiding (Set, max) import Polysemy import Polysemy.Input import qualified UnliftIO +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Team import Wire.API.Team.Conversation import Wire.API.Team.Member diff --git a/services/galley/src/Galley/Effects/TeamStore.hs b/services/galley/src/Galley/Effects/TeamStore.hs index bf2fdbb566..bc82c26808 100644 --- a/services/galley/src/Galley/Effects/TeamStore.hs +++ b/services/galley/src/Galley/Effects/TeamStore.hs @@ -80,12 +80,12 @@ import Data.Id import Data.Range import Galley.Effects.ListItems import Galley.Types.Teams -import Galley.Types.Teams.Intra import Imports import Polysemy import qualified Proto.TeamEvents as E import Wire.API.Error import Wire.API.Error.Galley +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Team import Wire.API.Team.Conversation import Wire.API.Team.Member (HardTruncationLimit, TeamMember, TeamMemberList) diff --git a/services/galley/test/integration/API.hs b/services/galley/test/integration/API.hs index a619dde1bb..7e2bf34276 100644 --- a/services/galley/test/integration/API.hs +++ b/services/galley/test/integration/API.hs @@ -66,7 +66,6 @@ import Federator.Discovery (DiscoveryFailure (..)) import Federator.MockServer import Galley.API.Mapping import Galley.Options (optFederator) -import Galley.Types.Conversations.Intra import Galley.Types.Conversations.Members import Imports import qualified Network.HTTP.Types.Status as HTTP @@ -94,6 +93,7 @@ import qualified Wire.API.Federation.API.Galley as F import Wire.API.Internal.Notification import Wire.API.Message import qualified Wire.API.Message as Message +import Wire.API.Routes.Internal.Galley.ConversationsIntra import Wire.API.Routes.MultiTablePaging import Wire.API.Routes.Version import Wire.API.Routes.Versioned diff --git a/services/galley/test/integration/API/Federation.hs b/services/galley/test/integration/API/Federation.hs index 99e90443ba..39c03e7fee 100644 --- a/services/galley/test/integration/API/Federation.hs +++ b/services/galley/test/integration/API/Federation.hs @@ -41,7 +41,6 @@ import Data.Time.Clock import Data.Timeout (TimeoutUnit (..), (#)) import Data.UUID.V4 (nextRandom) import Federator.MockServer -import Galley.Types.Conversations.Intra import Imports import Test.QuickCheck (arbitrary, generate) import Test.Tasty @@ -59,6 +58,7 @@ import qualified Wire.API.Federation.API.Galley as FedGalley import Wire.API.Federation.Component import Wire.API.Internal.Notification import Wire.API.Message +import Wire.API.Routes.Internal.Galley.ConversationsIntra import Wire.API.User.Client (PubClient (..)) import Wire.API.User.Profile diff --git a/services/galley/test/integration/API/Teams.hs b/services/galley/test/integration/API/Teams.hs index c930f65d90..097538d205 100644 --- a/services/galley/test/integration/API/Teams.hs +++ b/services/galley/test/integration/API/Teams.hs @@ -64,7 +64,6 @@ import qualified Galley.Env as Galley import Galley.Options (optSettings, setEnableIndexedBillingTeamMembers, setFeatureFlags, setMaxConvSize, setMaxFanoutSize) import Galley.Types.Conversations.Roles import Galley.Types.Teams -import Galley.Types.Teams.Intra as TeamsIntra import Imports import Network.HTTP.Types.Status (status403) import qualified Network.Wai.Utilities.Error as Error @@ -84,6 +83,7 @@ import Wire.API.Conversation.Protocol import Wire.API.Conversation.Role import Wire.API.Event.Team import Wire.API.Internal.Notification hiding (target) +import Wire.API.Routes.Internal.Galley.TeamsIntra as TeamsIntra import Wire.API.Team import Wire.API.Team.Export (TeamExportUser (..)) import qualified Wire.API.Team.Feature as Public diff --git a/services/galley/test/integration/API/Util.hs b/services/galley/test/integration/API/Util.hs index 97ecd69f67..c2db9ef395 100644 --- a/services/galley/test/integration/API/Util.hs +++ b/services/galley/test/integration/API/Util.hs @@ -80,10 +80,8 @@ import GHC.TypeNats import Galley.Intra.User (chunkify) import qualified Galley.Options as Opts import qualified Galley.Run as Run -import Galley.Types.Conversations.Intra import Galley.Types.Conversations.One2One import qualified Galley.Types.Teams as Team -import Galley.Types.Teams.Intra import Galley.Types.UserList import Imports import qualified Network.HTTP.Client as HTTP @@ -127,7 +125,9 @@ import Wire.API.MLS.Serialisation import Wire.API.Message import qualified Wire.API.Message.Proto as Proto import Wire.API.Routes.Internal.Brig.Connection +import Wire.API.Routes.Internal.Galley.ConversationsIntra import qualified Wire.API.Routes.Internal.Galley.TeamFeatureNoConfigMulti as Multi +import Wire.API.Routes.Internal.Galley.TeamsIntra import Wire.API.Routes.MultiTablePaging import Wire.API.Routes.Version import Wire.API.Team diff --git a/services/spar/src/Spar/API.hs b/services/spar/src/Spar/API.hs index 98edfd8ccb..2dd31ac448 100644 --- a/services/spar/src/Spar/API.hs +++ b/services/spar/src/Spar/API.hs @@ -101,6 +101,7 @@ import Spar.Sem.VerdictFormatStore (VerdictFormatStore) import qualified Spar.Sem.VerdictFormatStore as VerdictFormatStore import System.Logger (Msg) import qualified URI.ByteString as URI +import Wire.API.Routes.Internal.Spar import Wire.API.Routes.Public.Spar import Wire.API.User import Wire.API.User.IdentityProvider diff --git a/tools/stern/src/Stern/API.hs b/tools/stern/src/Stern/API.hs index dfd69cc42b..d743c45e18 100644 --- a/tools/stern/src/Stern/API.hs +++ b/tools/stern/src/Stern/API.hs @@ -44,7 +44,6 @@ import Data.String.Conversions (cs) import Data.Text (unpack) import qualified Data.Text as T import GHC.TypeLits (KnownSymbol) -import qualified Galley.Types.Teams.Intra as Team import Imports hiding (head) import Network.HTTP.Types import Network.Wai @@ -63,6 +62,7 @@ import Util.Options import Wire.API.Connection import Wire.API.Routes.Internal.Brig.Connection (ConnectionStatus) import qualified Wire.API.Routes.Internal.Brig.EJPD as EJPD +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Routes.Named (Named (Named)) import Wire.API.Team.Feature hiding (setStatus) import Wire.API.Team.SearchVisibility diff --git a/tools/stern/src/Stern/Intra.hs b/tools/stern/src/Stern/Intra.hs index fde2442b39..f4900e62fa 100644 --- a/tools/stern/src/Stern/Intra.hs +++ b/tools/stern/src/Stern/Intra.hs @@ -86,8 +86,6 @@ import Data.Text (strip) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Data.Text.Lazy (pack) import GHC.TypeLits (KnownSymbol) -import Galley.Types.Teams.Intra -import qualified Galley.Types.Teams.Intra as Team import Imports import Network.HTTP.Types.Method import Network.HTTP.Types.Status hiding (statusCode) @@ -104,6 +102,8 @@ import Wire.API.Internal.Notification import Wire.API.Properties import Wire.API.Routes.Internal.Brig.Connection import qualified Wire.API.Routes.Internal.Brig.EJPD as EJPD +import Wire.API.Routes.Internal.Galley.TeamsIntra +import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team import Wire.API.Routes.Version import Wire.API.Routes.Versioned import Wire.API.Team diff --git a/tools/stern/src/Stern/Types.hs b/tools/stern/src/Stern/Types.hs index b4300b1889..0135fd1466 100644 --- a/tools/stern/src/Stern/Types.hs +++ b/tools/stern/src/Stern/Types.hs @@ -35,10 +35,10 @@ import Data.Range import qualified Data.Schema as S import qualified Data.Swagger as Swagger import Galley.Types.Teams -import Galley.Types.Teams.Intra (TeamData) import Imports import Servant.API import Wire.API.Properties +import Wire.API.Routes.Internal.Galley.TeamsIntra (TeamData) import Wire.API.Team.Member import Wire.API.Team.Permission From b22753bf2c446d47a9ed978c9ddeb8c3a5f4c403 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 15 Feb 2023 16:10:56 +0100 Subject: [PATCH 02/12] Delete unused import --- libs/wire-api/src/Wire/API/Routes/Public/Spar.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs index cc196a3b30..f060f6b961 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs @@ -36,7 +36,6 @@ import Wire.API.Error.Brig import Wire.API.Routes.Internal.Spar import Wire.API.Routes.Public import Wire.API.SwaggerServant -import Wire.API.User (ScimUserInfos, UserSet) import Wire.API.User.IdentityProvider import Wire.API.User.Saml import Wire.API.User.Scim From 085bf0b94242076f84bde83f78558a34c406e6e3 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 15 Feb 2023 16:41:13 +0100 Subject: [PATCH 03/12] Kill one orphan --- libs/types-common/src/Data/Json/Util.hs | 10 ---------- .../Wire/API/Routes/Internal/Galley/TeamsIntra.hs | 13 +++++++++++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/libs/types-common/src/Data/Json/Util.hs b/libs/types-common/src/Data/Json/Util.hs index edf41b9c60..92ba6bb8e4 100644 --- a/libs/types-common/src/Data/Json/Util.hs +++ b/libs/types-common/src/Data/Json/Util.hs @@ -61,7 +61,6 @@ import qualified Data.ByteString.Base64.URL as B64U import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Conversion as BS import qualified Data.ByteString.Lazy as L -import qualified Data.Currency as Currency import Data.Fixed import Data.Schema import Data.String.Conversions (cs) @@ -270,12 +269,3 @@ fromBase64Text = B64.decode . Text.encodeUtf8 toBase64Text :: ByteString -> Text toBase64Text = Text.decodeUtf8 . B64.encode - --- TODO: Find a better module for this -instance ToSchema Currency.Alpha where - schema = mkSchema docs parseJSON (pure . toJSON) - where - docs = - swaggerDoc @Text - & S.schema . S.description ?~ "ISO 4217 alphabetic codes" - & S.schema . S.example ?~ "EUR" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs index 49eb17ab0b..5c1a38362f 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs @@ -28,11 +28,12 @@ module Wire.API.Routes.Internal.Galley.TeamsIntra ) where +import Control.Lens ((?~)) import Data.Aeson import qualified Data.Currency as Currency import Data.Json.Util import qualified Data.Schema as S -import qualified Data.Swagger as Swagger hiding (schema) +import qualified Data.Swagger as Swagger import Data.Time (UTCTime) import Imports import Test.QuickCheck.Arbitrary (Arbitrary) @@ -90,7 +91,15 @@ instance S.ToSchema TeamStatusUpdate where S.object "TeamStatusUpdate" $ TeamStatusUpdate <$> tuStatus S..= S.field "status" S.schema - <*> tuCurrency S..= S.maybe_ (S.optField "currency" S.schema) + <*> tuCurrency S..= S.maybe_ (S.optField "currency" currencyAlphaSchema) + where + currencyAlphaSchema :: S.ValueSchema S.NamedSwaggerDoc Currency.Alpha + currencyAlphaSchema = S.mkSchema docs parseJSON (pure . toJSON) + where + docs = + S.swaggerDoc @Text + & Swagger.schema . Swagger.description ?~ "ISO 4217 alphabetic codes" + & Swagger.schema . Swagger.example ?~ "EUR" newtype TeamName = TeamName {tnName :: Text} From 74c32a80474521ac576979ed4223c103f68a6c06 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 15 Feb 2023 16:53:28 +0100 Subject: [PATCH 04/12] Cleanup --- libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs | 2 +- .../API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs | 6 +++--- .../src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs | 6 +++--- libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs | 2 +- libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs index 1bae089ea6..b8835da44c 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs @@ -392,4 +392,4 @@ type IFeatureNoConfigMultiGet f = swaggerDoc :: Swagger swaggerDoc = toSwagger (Proxy @InternalAPI) - & info . title .~ "Wire-Server internal cannon API" + & info . title .~ "Wire-Server internal galley API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs index c45e17f478..40c9a1a094 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs @@ -33,7 +33,7 @@ newtype TeamFeatureNoConfigMultiRequest = TeamFeatureNoConfigMultiRequest { teams :: [TeamId] } deriving (Show, Eq) - deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema TeamFeatureNoConfigMultiRequest) + deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema TeamFeatureNoConfigMultiRequest instance ToSchema TeamFeatureNoConfigMultiRequest where schema = @@ -45,7 +45,7 @@ newtype TeamFeatureNoConfigMultiResponse cfg = TeamFeatureNoConfigMultiResponse { teamsStatuses :: [TeamStatus cfg] } deriving (Show, Eq) - deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema (TeamFeatureNoConfigMultiResponse cfg)) + deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema (TeamFeatureNoConfigMultiResponse cfg) instance ToSchema (TeamFeatureNoConfigMultiResponse cfg) where schema = @@ -58,7 +58,7 @@ data TeamStatus cfg = TeamStatus status :: Public.FeatureStatus } deriving (Show, Eq) - deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema (TeamStatus cfg)) + deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema (TeamStatus cfg) instance ToSchema (TeamStatus cfg) where schema = diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs index 5c1a38362f..2729485912 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs @@ -84,7 +84,7 @@ data TeamStatusUpdate = TeamStatusUpdate -- TODO: Remove Currency selection once billing supports currency changes after team creation } deriving (Eq, Show, Generic) - deriving (ToJSON, FromJSON, Swagger.ToSchema) via (S.Schema TeamStatusUpdate) + deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema TeamStatusUpdate instance S.ToSchema TeamStatusUpdate where schema = @@ -104,7 +104,7 @@ instance S.ToSchema TeamStatusUpdate where newtype TeamName = TeamName {tnName :: Text} deriving (Eq, Show, Generic) - deriving (ToJSON, FromJSON, Swagger.ToSchema) via (S.Schema TeamName) + deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema TeamName instance S.ToSchema TeamName where schema = @@ -118,7 +118,7 @@ data GuardLegalholdPolicyConflicts = GuardLegalholdPolicyConflicts } deriving (Show, Eq, Generic) deriving (Arbitrary) via (GenericUniform GuardLegalholdPolicyConflicts) - deriving (ToJSON, FromJSON, Swagger.ToSchema) via (S.Schema GuardLegalholdPolicyConflicts) + deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema GuardLegalholdPolicyConflicts instance S.ToSchema GuardLegalholdPolicyConflicts where schema = diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs b/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs index 6a32b669cc..785313aca0 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs @@ -43,4 +43,4 @@ type InternalLegalHoldAPI = swaggerDoc :: Swagger swaggerDoc = toSwagger (Proxy @InternalLegalHoldAPI) - & info . title .~ "Wire-Server internal cargohold API" + & info . title .~ "Wire-Server internal legalhold API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs index 967644acb3..6c50058640 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs @@ -22,4 +22,4 @@ type APIINTERNAL = swaggerDoc :: Swagger swaggerDoc = toSwagger (Proxy @APIINTERNAL) - & info . title .~ "Wire-Server internal cannon API" + & info . title .~ "Wire-Server internal spar API" From 1942770b5983833665008e1e2383a55a1c1ffdcc Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 15 Feb 2023 17:15:19 +0100 Subject: [PATCH 05/12] Better name --- libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs | 4 ++-- libs/wire-api/src/Wire/API/Routes/Public/Spar.hs | 2 +- services/spar/src/Spar/API.hs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs index 6c50058640..5700f2991d 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs @@ -10,7 +10,7 @@ import Wire.API.SwaggerServant import Wire.API.User import Wire.API.User.Saml -type APIINTERNAL = +type InternalAPI = SwaggerTag "spar" :> "i" :> ( "status" :> Get '[JSON] NoContent @@ -21,5 +21,5 @@ type APIINTERNAL = swaggerDoc :: Swagger swaggerDoc = - toSwagger (Proxy @APIINTERNAL) + toSwagger (Proxy @InternalAPI) & info . title .~ "Wire-Server internal spar API" diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs index f060f6b961..4af44325db 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs @@ -49,7 +49,7 @@ type API = "sso" :> APISSO :<|> "identity-providers" :> APIIDP :<|> "scim" :> APIScim - :<|> OmitDocs :> APIINTERNAL + :<|> OmitDocs :> InternalAPI type DeprecateSSOAPIV1 = Description diff --git a/services/spar/src/Spar/API.hs b/services/spar/src/Spar/API.hs index 2dd31ac448..0d07dabafb 100644 --- a/services/spar/src/Spar/API.hs +++ b/services/spar/src/Spar/API.hs @@ -215,7 +215,7 @@ apiINTERNAL :: ScimUserTimesStore ] r => - ServerT APIINTERNAL (Sem r) + ServerT InternalAPI (Sem r) apiINTERNAL = internalStatus :<|> internalDeleteTeam From 8624fe83ffde71e1a38e8e9f77682bfaa231e741 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 15 Feb 2023 17:15:29 +0100 Subject: [PATCH 06/12] Remove done todo --- libs/wire-api/src/Wire/API/Team/Feature.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 31264d3985..1b5179e05e 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -469,7 +469,6 @@ instance ToSchema LockStatus where element "unlocked" LockStatusUnlocked ] --- TODO: Check if this generic instantiation is/looks good instance S.ToParamSchema LockStatus instance ToByteString LockStatus where From 13e157a17bdc15f0ac596d66f59dfc70beea841a Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 16 Feb 2023 08:22:35 +0100 Subject: [PATCH 07/12] Play safe: Use the existing JSON representation of LegalholdProtectee --- libs/wire-api/src/Wire/API/Team/LegalHold.hs | 75 ++++++++------------ 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/LegalHold.hs b/libs/wire-api/src/Wire/API/Team/LegalHold.hs index 69c97b0d9e..d6794ae92a 100644 --- a/libs/wire-api/src/Wire/API/Team/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Team/LegalHold.hs @@ -1,5 +1,4 @@ {-# LANGUAGE StrictData #-} -{-# LANGUAGE TemplateHaskell #-} -- This file is part of the Wire Server implementation. -- @@ -30,11 +29,13 @@ module Wire.API.Team.LegalHold ) where -import Control.Lens hiding (element, enum, (.=)) +import Control.Lens (at, (?~)) +import qualified Data.Aeson as A import qualified Data.Aeson.Types as A import Data.Id import Data.LegalHold import Data.Misc +import Data.Proxy import Data.Schema import qualified Data.Swagger as S hiding (info) import Deriving.Aeson @@ -210,12 +211,6 @@ instance ToSchema ApproveLegalHoldForUserRequest where ----------------------------------------------------------------------- -data LegalholdProtecteeTag - = ProtectedUserTag - | UnprotectedBotTag - | LegalholdPlusFederationNotImplementedTag - deriving (Eq, Enum, Bounded) - -- | Bots are not protected to be potentially recorded by legalhold devices. data LegalholdProtectee = ProtectedUser UserId @@ -228,45 +223,37 @@ data LegalholdProtectee deriving (Show, Eq, Ord, Generic) deriving (Arbitrary) via (GenericUniform LegalholdProtectee) -$(makePrisms ''LegalholdProtectee) +instance ToJSON LegalholdProtectee -- {"tag":"ProtectedUser","contents":"110a187a-be5b-11eb-8f47-370bc8e40f35"} -- {"tag":"UnprotectedBot"} -- {"tag":"LegalholdPlusFederationNotImplemented"} +instance FromJSON LegalholdProtectee + instance ToSchema LegalholdProtectee where - schema :: ValueSchema NamedSwaggerDoc LegalholdProtectee - schema = - object "LegalholdProtectee" $ - fromTagged - <$> toTagged - .= bind - (fst .= field "tag" tagSchema) - (snd .= fieldOver _1 "value" untaggedSchema) + -- Generated mixed-sums are hard to cover: Just use their existing JSON + -- representation and add handwritten Swagger docs + schema = mkSchema docs A.parseJSON (pure . A.toJSON) where - toTagged :: LegalholdProtectee -> (LegalholdProtecteeTag, LegalholdProtectee) - toTagged d@(ProtectedUser _) = (ProtectedUserTag, d) - toTagged d@UnprotectedBot = (UnprotectedBotTag, d) - toTagged d@LegalholdPlusFederationNotImplemented = (LegalholdPlusFederationNotImplementedTag, d) - - fromTagged :: (LegalholdProtecteeTag, LegalholdProtectee) -> LegalholdProtectee - fromTagged = snd - - untaggedSchema = dispatch $ \case - ProtectedUserTag -> tag _ProtectedUser (unnamed schema) - UnprotectedBotTag -> tag _UnprotectedBot null_ - LegalholdPlusFederationNotImplementedTag -> tag _LegalholdPlusFederationNotImplemented null_ - - tagSchema :: ValueSchema NamedSwaggerDoc LegalholdProtecteeTag - tagSchema = - enum @Text "LegalholdProtecteeTag" $ - mconcat - [ element "ProtectedUser" ProtectedUserTag, - element "UnprotectedBot" UnprotectedBotTag, - element "LegalholdPlusFederationNotImplemented" LegalholdPlusFederationNotImplementedTag - ] - -deriving via (Schema LegalholdProtectee) instance (ToJSON LegalholdProtectee) - -deriving via (Schema LegalholdProtectee) instance (FromJSON LegalholdProtectee) - -deriving via (Schema LegalholdProtectee) instance (S.ToSchema LegalholdProtectee) + docs :: NamedSwaggerDoc + docs = + pure $ + S.NamedSchema (Just "LegalholdProtectee") $ + mempty + & S.type_ ?~ S.SwaggerObject + & S.properties . at "tag" + ?~ S.Inline + ( mempty + & S.type_ ?~ S.SwaggerString + & S.enum_ + ?~ [ A.toJSON ("ProtectedUser" :: String), + A.toJSON ("UnprotectedBot" :: String), + A.toJSON ("LegalholdPlusFederationNotImplemented" :: String) + ] + ) + & S.properties . at "contents" + ?~ S.Inline + ( S.toSchema (Proxy @UserId) + & S.description + ?~ "A UserId for ProtectedUser, otherwise empty / null." + ) From f1b6fd20f48eb21ad10f0bfc89d20c038cb4d50b Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 16 Feb 2023 08:52:24 +0100 Subject: [PATCH 08/12] Add some JSON roundtrip tests --- .../src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs | 4 ++++ libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs index 2729485912..1fb473fced 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs @@ -49,6 +49,7 @@ data TeamStatus | Suspended | PendingActive deriving (Eq, Show, Generic) + deriving (Arbitrary) via GenericUniform TeamStatus deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema TeamStatus instance S.ToSchema TeamStatus where @@ -68,6 +69,7 @@ data TeamData = TeamData tdStatusTime :: !(Maybe UTCTime) -- This needs to be a Maybe due to backwards compatibility } deriving (Eq, Show, Generic) + deriving (Arbitrary) via GenericUniform TeamData deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema TeamData instance S.ToSchema TeamData where @@ -84,6 +86,7 @@ data TeamStatusUpdate = TeamStatusUpdate -- TODO: Remove Currency selection once billing supports currency changes after team creation } deriving (Eq, Show, Generic) + deriving (Arbitrary) via GenericUniform TeamStatusUpdate deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema TeamStatusUpdate instance S.ToSchema TeamStatusUpdate where @@ -104,6 +107,7 @@ instance S.ToSchema TeamStatusUpdate where newtype TeamName = TeamName {tnName :: Text} deriving (Eq, Show, Generic) + deriving (Arbitrary) via GenericUniform TeamName deriving (ToJSON, FromJSON, Swagger.ToSchema) via S.Schema TeamName instance S.ToSchema TeamName where diff --git a/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs b/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs index 304bc7603a..2cb462e9d5 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs @@ -312,7 +312,11 @@ tests = testRoundTrip @User.Search.TeamContact, testRoundTrip @(Wrapped.Wrapped "some_int" Int), testRoundTrip @Conversation.Action.SomeConversationAction, - testRoundTrip @TeamsIntra.GuardLegalholdPolicyConflicts + testRoundTrip @TeamsIntra.GuardLegalholdPolicyConflicts, + testRoundTrip @TeamsIntra.TeamStatus, + testRoundTrip @TeamsIntra.TeamStatusUpdate, + testRoundTrip @TeamsIntra.TeamData, + testRoundTrip @TeamsIntra.TeamName ] testRoundTrip :: From 30b80ef634e93dc96fd58d2a3c7e1d011b693e79 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 16 Feb 2023 14:43:22 +0100 Subject: [PATCH 09/12] Tag all Swagger paths --- libs/wire-api/src/Wire/API/SwaggerServant.hs | 29 +++++++++++++++---- .../brig/docs/swagger-internal-endpoints.md | 5 ++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/libs/wire-api/src/Wire/API/SwaggerServant.hs b/libs/wire-api/src/Wire/API/SwaggerServant.hs index c0ea0649e2..738bb653a8 100644 --- a/libs/wire-api/src/Wire/API/SwaggerServant.hs +++ b/libs/wire-api/src/Wire/API/SwaggerServant.hs @@ -6,6 +6,7 @@ module Wire.API.SwaggerServant where import Control.Lens +import qualified Data.HashMap.Strict.InsOrd as InsOrdMap import qualified Data.HashSet.InsOrd as InsOrdSet import Data.Metrics.Servant import Data.Proxy @@ -39,14 +40,32 @@ import Servant.Swagger (HasSwagger (toSwagger)) -- @ data SwaggerTag tag +-- | Adjust `Swagger` according to its `SwaggerTag` +-- +-- Unfortunately, paths are stored as keys in a `InsOrdMap.InsOrdHashMap`. +-- Because those have to be unique, prefix them with the @tag@ (service name.) +-- Otherwise, the `Monoid` instance of `Swagger.Swagger` would throw duplicated +-- paths away. The @tag@ is also used as a `Swagger.TagName` for all +-- `Swagger.Operations`. instance (HasSwagger b, KnownSymbol tag) => HasSwagger (SwaggerTag tag :> b) where - toSwagger _ = prependTitle (T.pack (symbolVal (Proxy @tag))) $ toSwagger (Proxy :: Proxy b) + toSwagger _ = tagSwagger $ toSwagger (Proxy :: Proxy b) where - prependTitle :: Text -> S.Swagger -> S.Swagger - prependTitle tagName s = s & allOperations . S.tags <>~ tag tagName + tagString :: String + tagString = symbolVal (Proxy @tag) + + tagSwagger :: S.Swagger -> S.Swagger + tagSwagger s = + s + & S.paths .~ tagPaths s + & allOperations . S.tags <>~ tag + + tag :: InsOrdSet.InsOrdHashSet S.TagName + tag = InsOrdSet.singleton @S.TagName (T.pack tagString) - tag :: Text -> InsOrdSet.InsOrdHashSet S.TagName - tag tagName = InsOrdSet.singleton @S.TagName tagName + tagPaths :: S.Swagger -> InsOrdMap.InsOrdHashMap FilePath S.PathItem + tagPaths s = + let m = s ^. S.paths + in InsOrdMap.mapKeys (\k -> "/<" ++ tagString ++ ">" ++ k) m instance HasServer api ctx => HasServer (SwaggerTag tag :> api) ctx where type ServerT (SwaggerTag tag :> api) m = ServerT api m diff --git a/services/brig/docs/swagger-internal-endpoints.md b/services/brig/docs/swagger-internal-endpoints.md index be8d383d4d..dfa7fe8bcf 100644 --- a/services/brig/docs/swagger-internal-endpoints.md +++ b/services/brig/docs/swagger-internal-endpoints.md @@ -5,3 +5,8 @@ between services and site operators for forensics. Request execution does not work as Swagger expects a single target host whereas these endpoints are served by multiple hosts. + +Displayed paths are prefixed by the service name (e.g. `/`). This is +necessary because Swagger does not expect more than one endpoint for a given +path and method. Though, some paths (e.g. `/i/status`) exist on more than one +service. To use the a path, remove the prefix. From 0c5e9dcbef144c82217c449393c50d3a400df521 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 16 Feb 2023 14:45:15 +0100 Subject: [PATCH 10/12] Add changelog --- changelog.d/2-features/add-missing-internal-endpoints-to-swagger | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2-features/add-missing-internal-endpoints-to-swagger diff --git a/changelog.d/2-features/add-missing-internal-endpoints-to-swagger b/changelog.d/2-features/add-missing-internal-endpoints-to-swagger new file mode 100644 index 0000000000..54a52e77d0 --- /dev/null +++ b/changelog.d/2-features/add-missing-internal-endpoints-to-swagger @@ -0,0 +1 @@ +Add internal endpoints of `cargohold`, `galley`, `legalhold` and `spar` to the Swagger docs for internal endpoints. From b5cf4220bd860cd4d7052bf72f4721585dd7354e Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 16 Feb 2023 15:57:55 +0100 Subject: [PATCH 11/12] Add copyright notices --- .../src/Wire/API/Routes/Internal/Galley.hs | 17 +++++++++++++++++ .../Internal/Galley/ConversationsIntra.hs | 17 +++++++++++++++++ .../API/Routes/Internal/Galley/TeamsIntra.hs | 7 +++---- .../src/Wire/API/Routes/Internal/Spar.hs | 17 +++++++++++++++++ libs/wire-api/src/Wire/API/SwaggerServant.hs | 17 +++++++++++++++++ .../brig/src/Brig/Effects/GalleyProvider.hs | 16 ++++++++++++++++ .../brig/src/Brig/Effects/GalleyProvider/RPC.hs | 16 ++++++++++++++++ 7 files changed, 103 insertions(+), 4 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs index b8835da44c..1f9b9ba9ef 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs @@ -1,3 +1,20 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2023 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 Wire.API.Routes.Internal.Galley where import Control.Lens ((.~)) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs index b1c07e1052..a296747691 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs @@ -1,3 +1,20 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2023 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 Wire.API.Routes.Internal.Galley.ConversationsIntra ( DesiredMembership (..), Actor (..), diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs index 1fb473fced..3b64ef876c 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs @@ -1,7 +1,3 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE OverloadedStrings #-} - -- This file is part of the Wire Server implementation. -- -- Copyright (C) 2022 Wire Swiss GmbH @@ -18,6 +14,9 @@ -- -- You should have received a copy of the GNU Affero General Public License along -- with this program. If not, see . +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} module Wire.API.Routes.Internal.Galley.TeamsIntra ( TeamStatus (..), diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs index 5700f2991d..b9a76a4b94 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs @@ -1,3 +1,20 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2023 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 Wire.API.Routes.Internal.Spar where import Control.Lens diff --git a/libs/wire-api/src/Wire/API/SwaggerServant.hs b/libs/wire-api/src/Wire/API/SwaggerServant.hs index 738bb653a8..ef7adb1c79 100644 --- a/libs/wire-api/src/Wire/API/SwaggerServant.hs +++ b/libs/wire-api/src/Wire/API/SwaggerServant.hs @@ -1,3 +1,20 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2023 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 . + -- | Servant combinators related to Swagger docs module Wire.API.SwaggerServant ( SwaggerTag, diff --git a/services/brig/src/Brig/Effects/GalleyProvider.hs b/services/brig/src/Brig/Effects/GalleyProvider.hs index 1ef19c495d..2457b026ef 100644 --- a/services/brig/src/Brig/Effects/GalleyProvider.hs +++ b/services/brig/src/Brig/Effects/GalleyProvider.hs @@ -1,3 +1,19 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2023 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 . {-# LANGUAGE TemplateHaskell #-} module Brig.Effects.GalleyProvider where diff --git a/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs b/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs index d7e3f96825..3abc308051 100644 --- a/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs +++ b/services/brig/src/Brig/Effects/GalleyProvider/RPC.hs @@ -1,3 +1,19 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2023 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 . {-# OPTIONS_GHC -Wno-unused-matches #-} module Brig.Effects.GalleyProvider.RPC where From 61e3d4474bbe57d9a0cfdf19ec02fe792f29ea56 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 16 Feb 2023 19:13:02 +0100 Subject: [PATCH 12/12] hi ci