Skip to content
1 change: 1 addition & 0 deletions changelog.d/1-api-changes/fs-926-add-global-team-conv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added global conversation type and GET endpoint (`GET /teams/:tid/conversations/global`).
10 changes: 10 additions & 0 deletions libs/bilge/src/Bilge/Assert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module Bilge.Assert
(===),
(=/=),
(=~=),
(=/~=),
assertResponse,
assertTrue,
assertTrue_,
Expand Down Expand Up @@ -141,6 +142,15 @@ f =/= g = Assertions $ tell [\r -> test " === " (/=) (f r) (g r)]
Assertions ()
f =~= g = Assertions $ tell [\r -> test " not in " contains (f r) (g r)]

-- | Tests the assertion that the left-hand side is **not** contained in the right-hand side.
-- If it is, actual values will be printed.
(=/~=) ::
(HasCallStack, Show a, Contains a) =>
(Response (Maybe Lazy.ByteString) -> a) ->
(Response (Maybe Lazy.ByteString) -> a) ->
Assertions ()
f =/~= g = Assertions $ tell [\r -> test " in " ((not .) . contains) (f r) (g r)]

-- | Most generic assertion on a request. If the test function evaluates to
-- @(Just msg)@ then the assertion fails with the error message @msg@.
assertResponse :: HasCallStack => (Response (Maybe Lazy.ByteString) -> Maybe String) -> Assertions ()
Expand Down
19 changes: 14 additions & 5 deletions libs/wire-api/src/Wire/API/Conversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module Wire.API.Conversation
ConversationMetadata (..),
defConversationMetadata,
Conversation (..),
conversationMetadataObjectSchema,
cnvType,
cnvCreator,
cnvAccess,
Expand Down Expand Up @@ -100,7 +101,7 @@ import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.Aeson as A
import qualified Data.ByteString.Lazy as LBS
import Data.Id
import Data.List.Extra (disjointOrd)
import Data.List.Extra (disjointOrd, enumerate)
import Data.List.NonEmpty (NonEmpty)
import Data.List1
import Data.Misc
Expand Down Expand Up @@ -436,6 +437,10 @@ data Access
LinkAccess
| -- | User can join knowing [changeable/revokable] code
CodeAccess
| -- | In MLS the user can join the global team conversation with their
-- | clients via an external commit, thereby inviting their own clients to
-- | join.
SelfInviteAccess
deriving stock (Eq, Ord, Bounded, Enum, Show, Generic)
deriving (Arbitrary) via (GenericUniform Access)
deriving (ToJSON, FromJSON, S.ToSchema) via Schema Access
Expand All @@ -448,7 +453,8 @@ instance ToSchema Access where
[ element "private" PrivateAccess,
element "invite" InviteAccess,
element "link" LinkAccess,
element "code" CodeAccess
element "code" CodeAccess,
element "self_invite" SelfInviteAccess
]

typeAccess :: Doc.DataType
Expand Down Expand Up @@ -496,6 +502,7 @@ defRole = activatedAccessRole

maybeRole :: ConvType -> Maybe (Set AccessRoleV2) -> Set AccessRoleV2
maybeRole SelfConv _ = privateAccessRole
maybeRole GlobalTeamConv _ = teamAccessRole
maybeRole ConnectConv _ = privateAccessRole
maybeRole One2OneConv _ = privateAccessRole
maybeRole RegularConv Nothing = defRole
Expand Down Expand Up @@ -578,7 +585,8 @@ data ConvType
| SelfConv
| One2OneConv
| ConnectConv
deriving stock (Eq, Show, Generic)
| GlobalTeamConv
deriving stock (Eq, Show, Generic, Enum, Bounded)
deriving (Arbitrary) via (GenericUniform ConvType)
deriving (FromJSON, ToJSON, S.ToSchema) via Schema ConvType

Expand All @@ -589,11 +597,12 @@ instance ToSchema ConvType where
[ element 0 RegularConv,
element 1 SelfConv,
element 2 One2OneConv,
element 3 ConnectConv
element 3 ConnectConv,
element 4 GlobalTeamConv
]

typeConversationType :: Doc.DataType
typeConversationType = Doc.int32 $ Doc.enum [0, 1, 2, 3]
typeConversationType = Doc.int32 $ Doc.enum $ fromIntegral . fromEnum <$> enumerate @ConvType

-- | Define whether receipts should be sent in the given conversation
-- This datatype is defined as an int32 but the Backend does not
Expand Down
5 changes: 5 additions & 0 deletions libs/wire-api/src/Wire/API/Conversation/Action.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import Wire.Arbitrary (Arbitrary (..))
-- individual effects per conversation action. See 'HasConversationActionEffects'.
type family ConversationAction (tag :: ConversationActionTag) :: * where
ConversationAction 'ConversationJoinTag = ConversationJoin
ConversationAction 'ConversationSelfInviteTag = ConvId
ConversationAction 'ConversationLeaveTag = ()
ConversationAction 'ConversationMemberUpdateTag = ConversationMemberUpdate
ConversationAction 'ConversationDeleteTag = ()
Expand Down Expand Up @@ -103,6 +104,7 @@ conversationActionSchema SConversationRenameTag = schema
conversationActionSchema SConversationMessageTimerUpdateTag = schema
conversationActionSchema SConversationReceiptModeUpdateTag = schema
conversationActionSchema SConversationAccessDataTag = schema
conversationActionSchema SConversationSelfInviteTag = schema

instance FromJSON SomeConversationAction where
parseJSON = A.withObject "SomeConversationAction" $ \ob -> do
Expand Down Expand Up @@ -150,6 +152,9 @@ conversationActionToEvent tag now quid qcnv action =
SConversationJoinTag ->
let ConversationJoin newMembers role = action
in EdMembersJoin $ SimpleMembers (map (`SimpleMember` role) (toList newMembers))
SConversationSelfInviteTag ->
-- this event will not be sent anyway so this is a dummy event
EdMembersJoin $ SimpleMembers []
SConversationLeaveTag ->
EdMembersLeave (QualifiedUserIdList [quid])
SConversationRemoveMembersTag ->
Expand Down
2 changes: 2 additions & 0 deletions libs/wire-api/src/Wire/API/Conversation/Action/Tag.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Wire.Arbitrary (Arbitrary (..))

data ConversationActionTag
= ConversationJoinTag
| ConversationSelfInviteTag
| ConversationLeaveTag
| ConversationRemoveMembersTag
| ConversationMemberUpdateTag
Expand All @@ -48,6 +49,7 @@ instance ToSchema ConversationActionTag where
enum @Text "ConversationActionTag" $
mconcat
[ element "ConversationJoinTag" ConversationJoinTag,
element "ConversationSelfInviteTag" ConversationSelfInviteTag,
element "ConversationLeaveTag" ConversationLeaveTag,
element "ConversationRemoveMembersTag" ConversationRemoveMembersTag,
element "ConversationMemberUpdateTag" ConversationMemberUpdateTag,
Expand Down
1 change: 1 addition & 0 deletions libs/wire-api/src/Wire/API/Conversation/Protocol.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module Wire.API.Conversation.Protocol
_ProtocolMLS,
_ProtocolProteus,
protocolSchema,
mlsDataSchema,
ConversationMLSData (..),
)
where
Expand Down
1 change: 1 addition & 0 deletions libs/wire-api/src/Wire/API/Conversation/Role.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module Wire.API.Conversation.Role
wireConvRoleNames,
roleNameWireAdmin,
roleNameWireMember,
roleToRoleName,

-- * Action
Action (..),
Expand Down
63 changes: 63 additions & 0 deletions libs/wire-api/src/Wire/API/MLS/GlobalTeamConversation.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- 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 <https://www.gnu.org/licenses/>.

module Wire.API.MLS.GlobalTeamConversation where

import Control.Lens ((?~))
import Data.Aeson (FromJSON, ToJSON)
import Data.Id
import Data.Qualified
import Data.Schema
import qualified Data.Swagger as S
import Imports
import Wire.API.Conversation hiding (Conversation)
import Wire.API.Conversation.Protocol
import Wire.Arbitrary (Arbitrary (..), GenericUniform (..))

-- | Public-facing global team conversation.
-- Membership is implicit. Every member of a team is part of it.
-- Protocol is also implicit: it's always MLS.
data GlobalTeamConversation = GlobalTeamConversation
{ gtcId :: Qualified ConvId,
gtcMlsMetadata :: ConversationMLSData,
gtcCreator :: Maybe UserId,
gtcAccess :: [Access],
gtcName :: Text,
gtcTeam :: TeamId
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform GlobalTeamConversation)
deriving (FromJSON, ToJSON, S.ToSchema) via Schema GlobalTeamConversation

instance ToSchema GlobalTeamConversation where
schema =
objectWithDocModifier
"GlobalTeamConversation"
(description ?~ "The global team conversation object as returned from the server")
$ GlobalTeamConversation
<$> gtcId .= field "qualified_id" schema
<*> gtcMlsMetadata .= mlsDataSchema
<*> gtcCreator
.= maybe_
( optFieldWithDocModifier
"creator"
(description ?~ "The creator's user ID")
schema
)
<*> gtcAccess .= field "access" (array schema)
<*> gtcName .= field "name" schema
<*> gtcTeam .= field "team" schema
13 changes: 13 additions & 0 deletions libs/wire-api/src/Wire/API/Routes/Public/Galley/Conversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Wire.API.Conversation.Role
import Wire.API.Error
import Wire.API.Error.Galley
import Wire.API.Event.Conversation
import Wire.API.MLS.GlobalTeamConversation
import Wire.API.MLS.PublicGroupState
import Wire.API.MLS.Servant
import Wire.API.Routes.MultiVerb
Expand Down Expand Up @@ -114,6 +115,18 @@ type ConversationAPI =
:> QualifiedCapture "cnv" ConvId
:> Get '[Servant.JSON] Conversation
)
:<|> Named
"get-global-team-conversation"
( Summary "Get the global conversation for a given team ID"
:> CanThrow 'ConvNotFound
:> CanThrow 'NotATeamMember
:> ZLocalUser
:> "teams"
:> Capture "tid" TeamId
:> "conversations"
:> "global"
:> Get '[Servant.JSON] GlobalTeamConversation
)
:<|> Named
"get-conversation-roles"
( Summary "Get existing roles available for the given conversation"
Expand Down
2 changes: 1 addition & 1 deletion libs/wire-api/src/Wire/API/Routes/Public/Galley/Team.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type TeamAPI =
"create-non-binding-team"
( Summary "Create a new non binding team"
-- FUTUREWORK: deprecated in https://github.com/wireapp/wire-server/pull/2607
:> ZUser
:> ZLocalUser
:> ZConn
:> CanThrow 'NotConnected
:> CanThrow 'UserBindingExists
Expand Down
1 change: 1 addition & 0 deletions libs/wire-api/wire-api.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ library
Wire.API.MLS.Credential
Wire.API.MLS.Epoch
Wire.API.MLS.Extension
Wire.API.MLS.GlobalTeamConversation
Wire.API.MLS.Group
Wire.API.MLS.GroupInfoBundle
Wire.API.MLS.KeyPackage
Expand Down
4 changes: 2 additions & 2 deletions nix/pkgs/mls-test-cli/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ rustPlatform.buildRustPackage rec {
src = fetchFromGitHub {
owner = "wireapp";
repo = "mls-test-cli";
sha256 = "sha256-/XQ/9oQTPkRqgMzDGRm+Oh9jgkdeDM1vRJ6/wEf2+bY=";
rev = "c6f80be2839ac1ed2894e96044541d1c3cf6ecdf";
sha256 = "sha256-FjgAcYdUr/ZWdQxbck2UEG6NEEQLuz0S4a55hrAxUs4=";
rev = "82fc148964ef5baa92a90d086fdc61adaa2b5dbf";
};
doCheck = false;
cargoSha256 = "sha256-AlZrxa7f5JwxxrzFBgeFSaYU6QttsUpfLYfq1HzsdbE=";
Expand Down
3 changes: 3 additions & 0 deletions services/brig/src/Brig/RPC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ x3 = limitRetries 3 <> exponentialBackoff 100000
zUser :: UserId -> Request -> Request
zUser = header "Z-User" . toByteString'

zClient :: ClientId -> Request -> Request
zClient = header "Z-Client" . toByteString'

remote :: ByteString -> Msg -> Msg
remote = field "remote"

Expand Down
14 changes: 13 additions & 1 deletion services/brig/test/integration/API/Provider.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1328,7 +1328,19 @@ createConvWithAccessRoles ars g u us =
. contentJson
. body (RequestBodyLBS (encode conv))
where
conv = NewConv us [] Nothing Set.empty ars Nothing Nothing Nothing roleNameWireAdmin ProtocolProteusTag Nothing
conv =
NewConv
us
[]
Nothing
Set.empty
ars
Nothing
Nothing
Nothing
roleNameWireAdmin
ProtocolProteusTag
Nothing

postMessage ::
Galley ->
Expand Down
13 changes: 12 additions & 1 deletion services/brig/test/integration/API/Team/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,18 @@ createTeamConv :: HasCallStack => Galley -> TeamId -> UserId -> [UserId] -> Mayb
createTeamConv g tid u us mtimer = do
let tinfo = Just $ ConvTeamInfo tid
let conv =
NewConv us [] Nothing (Set.fromList []) Nothing tinfo mtimer Nothing roleNameWireAdmin ProtocolProteusTag Nothing
NewConv
us
[]
Nothing
(Set.fromList [])
Nothing
tinfo
mtimer
Nothing
roleNameWireAdmin
ProtocolProteusTag
Nothing
r <-
post
( g
Expand Down
7 changes: 1 addition & 6 deletions services/brig/test/integration/API/TeamUserSearch.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{-# OPTIONS_GHC -Wno-unused-imports #-}

-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
Expand All @@ -24,16 +22,13 @@ import API.Team.Util (createPopulatedBindingTeamWithNamesAndHandles)
import API.User.Util (activateEmail, initiateEmailUpdateNoSend)
import Bilge (Manager, MonadHttp)
import qualified Brig.Options as Opt
import Brig.User.Search.TeamUserSearch (TeamUserSearchSortBy (..), TeamUserSearchSortOrder (..))
import Control.Monad.Catch (MonadCatch)
import Control.Retry ()
import Data.ByteString.Conversion (ToByteString (..), toByteString)
import Data.ByteString.Conversion (toByteString)
import Data.Handle (fromHandle)
import Data.Id (TeamId, UserId)
import qualified Data.Map.Strict as M
import Data.String.Conversions (cs)
import Imports
import System.Random
import System.Random.Shuffle (shuffleM)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertBool, assertEqual)
Expand Down
Loading