Skip to content

SQSERVICES-1012-backend-servantify-galley-team-member-api #2309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5-internal/pr-2309
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Team Member API has been migrated to Servant
5 changes: 3 additions & 2 deletions libs/galley-types/src/Galley/Types/Teams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ module Galley.Types.Teams
nInvitation,
legalHoldStatus,
TeamMemberList,
TeamMemberListOptPerms,
ListType (..),
newTeamMemberList,
teamMembers,
Expand Down Expand Up @@ -467,8 +468,8 @@ isTeamMember u = isJust . findTeamMember u
findTeamMember :: Foldable m => UserId -> m TeamMember -> Maybe TeamMember
findTeamMember u = find ((u ==) . view userId)

isTeamOwner :: TeamMember -> Bool
isTeamOwner tm = fullPermissions == (tm ^. permissions)
isTeamOwner :: TeamMemberOptPerms -> Bool
isTeamOwner tm = optionalPermissions tm == Just fullPermissions

-- | Use this to construct the condition expected by 'teamMemberJson', 'teamMemberListJson'
canSeePermsOf :: TeamMember -> TeamMember -> Bool
Expand Down
8 changes: 8 additions & 0 deletions libs/wire-api/src/Wire/API/Error/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import Wire.API.Conversation.Role
import Wire.API.Error
import qualified Wire.API.Error.Brig as BrigError
import Wire.API.Routes.API
import Wire.API.Team.Member
import Wire.API.Team.Permission
import Wire.API.Util.Aeson (CustomEncoded (..))

Expand Down Expand Up @@ -106,6 +107,8 @@ data GalleyError
| TooManyTeamMembersOnTeamWithLegalhold
| NoLegalHoldDeviceAllocated
| UserLegalHoldNotPending
| -- Team Member errors
BulkGetMemberLimitExceeded
deriving (Show, Eq, Generic)
deriving (FromJSON, ToJSON) via (CustomEncoded GalleyError)

Expand Down Expand Up @@ -247,6 +250,11 @@ type instance MapError 'NoLegalHoldDeviceAllocated = 'StaticError 404 "legalhold

type instance MapError 'LegalHoldCouldNotBlockConnections = 'StaticError 500 "legalhold-internal" "legal hold service: could not block connections when resolving policy conflicts."

--------------------------------------------------------------------------------
-- Team Member errors

type instance MapError 'BulkGetMemberLimitExceeded = 'StaticError 400 "too-many-uids" ("Can only process " `AppendSymbol` Show_ HardTruncationLimit `AppendSymbol` " user ids per request.")

--------------------------------------------------------------------------------
-- Authentication errors

Expand Down
153 changes: 153 additions & 0 deletions libs/wire-api/src/Wire/API/Routes/Public/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ import Wire.API.Team
import Wire.API.Team.Conversation
import Wire.API.Team.Feature
import Wire.API.Team.LegalHold
import Wire.API.Team.Member
import Wire.API.Team.Permission (Perm (..))
import Wire.API.Team.SearchVisibility (TeamSearchVisibilityView)
import qualified Wire.API.User as User

instance AsHeaders '[ConvId] Conversation Conversation where
toHeaders c = (I (qUnqualified (cnvQualifiedId c)) :* Nil, c)
Expand Down Expand Up @@ -166,6 +168,7 @@ type ServantAPI =
:<|> MLSAPI
:<|> CustomBackendAPI
:<|> LegalHoldAPI
:<|> TeamMemberAPI

type ConversationAPI =
Named
Expand Down Expand Up @@ -1489,6 +1492,156 @@ data GrantConsentResult

instance GSOP.Generic GrantConsentResult

type TeamMemberAPI =
Named
"get-team-members"
( Summary "Get team members"
:> CanThrow 'NotATeamMember
:> ZLocalUser
:> "teams"
:> Capture "tid" TeamId
:> "members"
:> QueryParam'
[ Optional,
Strict,
Description "Maximum results to be returned"
]
"maxResults"
(Range 1 HardTruncationLimit Int32)
:> Get '[JSON] TeamMemberListOptPerms
)
:<|> Named
"get-team-member"
( Summary "Get single team member"
:> CanThrow 'NotATeamMember
:> CanThrow 'TeamMemberNotFound
:> ZLocalUser
:> "teams"
:> Capture "tid" TeamId
:> "members"
:> Capture "uid" UserId
:> Get '[JSON] TeamMemberOptPerms
)
:<|> Named
"get-team-members-by-ids"
( Summary "Get team members by user id list"
:> Description "The `has_more` field in the response body is always `false`."
:> CanThrow 'NotATeamMember
:> CanThrow 'BulkGetMemberLimitExceeded
:> ZLocalUser
:> "teams"
:> Capture "tid" TeamId
:> "get-members-by-ids-using-post"
:> QueryParam'
[ Optional,
Strict,
Description "Maximum results to be returned"
]
"maxResults"
(Range 1 HardTruncationLimit Int32)
:> ReqBody '[JSON] User.UserIdList
:> Post '[JSON] TeamMemberListOptPerms
)
:<|> Named
"add-team-member"
( Summary "Add a new team member"
:> CanThrow 'InvalidPermissions
:> CanThrow 'NoAddToBinding
:> CanThrow 'NotATeamMember
:> CanThrow 'NotConnected
:> CanThrow OperationDenied
:> CanThrow 'TeamNotFound
:> CanThrow 'TooManyTeamMembers
:> CanThrow 'UserBindingExists
:> CanThrow 'TooManyTeamMembersOnTeamWithLegalhold
:> ZLocalUser
:> ZConn
:> "teams"
:> Capture "tid" TeamId
:> "members"
:> ReqBody '[JSON] NewTeamMember
:> MultiVerb1
'POST
'[JSON]
(RespondEmpty 200 "")
)
:<|> Named
"delete-team-member"
( Summary "Remove an existing team member"
:> CanThrow AuthenticationError
:> CanThrow 'AccessDenied
:> CanThrow 'TeamMemberNotFound
:> CanThrow 'TeamNotFound
:> CanThrow 'NotATeamMember
:> CanThrow OperationDenied
:> ZLocalUser
:> ZConn
:> "teams"
:> Capture "tid" TeamId
:> "members"
:> Capture "uid" UserId
:> ReqBody '[JSON] TeamMemberDeleteData
:> MultiVerb
'DELETE
'[JSON]
TeamMemberDeleteResultResponseType
TeamMemberDeleteResult
)
:<|> Named
"delete-non-binding-team-member"
( Summary "Remove an existing team member"
:> CanThrow AuthenticationError
:> CanThrow 'AccessDenied
:> CanThrow 'TeamMemberNotFound
:> CanThrow 'TeamNotFound
:> CanThrow 'NotATeamMember
:> CanThrow OperationDenied
:> ZLocalUser
:> ZConn
:> "teams"
:> Capture "tid" TeamId
:> "members"
:> Capture "uid" UserId
:> MultiVerb
'DELETE
'[JSON]
TeamMemberDeleteResultResponseType
TeamMemberDeleteResult
)
:<|> Named
"update-team-member"
( Summary "Update an existing team member"
:> CanThrow 'AccessDenied
:> CanThrow 'InvalidPermissions
:> CanThrow 'TeamNotFound
:> CanThrow 'TeamMemberNotFound
:> CanThrow 'NotATeamMember
:> CanThrow OperationDenied
:> ZLocalUser
:> ZConn
:> "teams"
:> Capture "tid" TeamId
:> "members"
:> ReqBody '[JSON] NewTeamMember
:> MultiVerb1
'PUT
'[JSON]
(RespondEmpty 200 "")
)

type TeamMemberDeleteResultResponseType =
'[ RespondEmpty 202 "Team member scheduled for deletion",
RespondEmpty 200 ""
]

data TeamMemberDeleteResult
= TeamMemberDeleteAccepted
| TeamMemberDeleteCompleted
deriving (Generic)
deriving (AsUnion TeamMemberDeleteResultResponseType) via GenericAsUnion TeamMemberDeleteResultResponseType TeamMemberDeleteResult

instance GSOP.Generic TeamMemberDeleteResult

-- This is a work-around for the fact that we sometimes want to send larger lists of user ids
-- in the filter query than fits the url length limit. For details, see
-- https://github.com/zinfra/backend-issues/issues/1248
Expand Down
7 changes: 0 additions & 7 deletions libs/wire-api/src/Wire/API/Swagger.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ 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
import qualified Wire.API.Team.Invitation as Team.Invitation
import qualified Wire.API.Team.Member as Team.Member
import qualified Wire.API.Team.Permission as Team.Permission
import qualified Wire.API.User as User
import qualified Wire.API.User.Activation as User.Activation
Expand Down Expand Up @@ -93,8 +92,6 @@ models =
Push.Token.modelPushTokenList,
Team.modelTeam,
Team.modelTeamList,
Team.modelNewNonBindingTeam,
Team.modelUpdateData,
Team.modelTeamDelete,
Team.Conversation.modelTeamConversation,
Team.Conversation.modelTeamConversationList,
Expand All @@ -118,10 +115,6 @@ models =
Team.Invitation.modelTeamInvitation,
Team.Invitation.modelTeamInvitationList,
Team.Invitation.modelTeamInvitationRequest,
Team.Member.modelTeamMember,
Team.Member.modelTeamMemberList,
Team.Member.modelNewTeamMember,
Team.Member.modelTeamMemberDelete,
Team.Permission.modelPermissions,
User.modelUserIdList,
User.modelUser,
Expand Down
27 changes: 6 additions & 21 deletions libs/wire-api/src/Wire/API/Team.hs
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,12 @@ module Wire.API.Team
-- * Swagger
modelTeam,
modelTeamList,
modelNewNonBindingTeam,
modelUpdateData,
modelTeamDelete,
)
where

import Control.Lens (makeLenses)
import Control.Lens (makeLenses, (?~))
import Data.Aeson (FromJSON, ToJSON, Value (..))
import Data.Aeson.Types (Parser)
import qualified Data.Attoparsec.ByteString as Atto (Parser, string)
Expand All @@ -90,7 +89,7 @@ import Imports
import Test.QuickCheck.Gen (suchThat)
import Wire.API.Arbitrary (Arbitrary (arbitrary), GenericUniform (..))
import Wire.API.Asset (AssetKey)
import Wire.API.Team.Member (TeamMember, modelTeamMember)
import Wire.API.Team.Member (TeamMember)

--------------------------------------------------------------------------------
-- Team
Expand Down Expand Up @@ -218,20 +217,6 @@ instance ToSchema NonBindingNewTeam where
sch :: ValueSchema SwaggerDoc (Range 1 127 [TeamMember])
sch = fromRange .= rangedSchema (array schema)

modelNewNonBindingTeam :: Doc.Model
modelNewNonBindingTeam = Doc.defineModel "newNonBindingTeam" $ do
Doc.description "Required data when creating new regular teams"
Doc.property "name" Doc.string' $
Doc.description "team name"
Doc.property "icon" Doc.string' $
Doc.description "team icon (asset ID)"
Doc.property "icon_key" Doc.string' $ do
Doc.description "team icon asset key"
Doc.optional
Doc.property "members" (Doc.unique $ Doc.array (Doc.ref modelTeamMember)) $ do
Doc.description "initial team member ids (between 1 and 127)"
Doc.optional

data NewTeam a = NewTeam
{ _newTeamName :: Range 1 256 Text,
_newTeamIcon :: Icon,
Expand All @@ -247,10 +232,10 @@ newNewTeam nme ico = NewTeam nme ico Nothing Nothing
newTeamObjectSchema :: ValueSchema SwaggerDoc a -> ObjectSchema SwaggerDoc (NewTeam a)
newTeamObjectSchema sch =
NewTeam
<$> _newTeamName .= field "name" schema
<*> _newTeamIcon .= field "icon" schema
<*> _newTeamIconKey .= maybe_ (optField "icon_key" schema)
<*> _newTeamMembers .= maybe_ (optField "members" sch)
<$> _newTeamName .= fieldWithDocModifier "name" (description ?~ "team name") schema
<*> _newTeamIcon .= fieldWithDocModifier "icon" (description ?~ "team icon (asset ID)") schema
<*> _newTeamIconKey .= maybe_ (optFieldWithDocModifier "icon_key" (description ?~ "team icon asset key") schema)
<*> _newTeamMembers .= maybe_ (optFieldWithDocModifier "members" (description ?~ "initial team member ids (between 1 and 127)") sch)

--------------------------------------------------------------------------------
-- TeamUpdateData
Expand Down
Loading