Skip to content

Commit

Permalink
SQSERVICES-1012-backend-servantify-galley-team-member-api (#2309)
Browse files Browse the repository at this point in the history
  • Loading branch information
battermann authored May 9, 2022
1 parent 8f1f841 commit a9b5d03
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 384 deletions.
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 @@ -57,8 +57,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 @@ -167,6 +169,7 @@ type ServantAPI =
:<|> MLSAPI
:<|> CustomBackendAPI
:<|> LegalHoldAPI
:<|> TeamMemberAPI

type ConversationAPI =
Named
Expand Down Expand Up @@ -1514,6 +1517,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

0 comments on commit a9b5d03

Please sign in to comment.