Skip to content

Commit

Permalink
[WPB-12098] add inviter email to personal user invitation (#4332)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisx authored Nov 8, 2024
1 parent 30dbb07 commit 97cf75c
Show file tree
Hide file tree
Showing 17 changed files with 209 additions and 101 deletions.
1 change: 1 addition & 0 deletions changelog.d/2-features/WPB-12098
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added inviter's email to `GET /teams/invitation/info` endpoint.
6 changes: 6 additions & 0 deletions integration/test/API/Brig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,12 @@ listInvitations user tid = do
req <- baseRequest user Brig Versioned $ joinHttpPath ["teams", tid, "invitations"]
submit "GET" req

-- | https://staging-nginz-https.zinfra.io/v7/api/swagger-ui/#/default/get-team-invitation-info
getInvitationByCode :: (HasCallStack, MakesValue user) => user -> String -> App Response
getInvitationByCode user code = do
req <- baseRequest user Brig Versioned $ joinHttpPath ["teams", "invitations", "info"]
submit "GET" (req & addQueryParams [("code", code)])

passwordReset :: (HasCallStack, MakesValue domain) => domain -> String -> App Response
passwordReset domain email = do
req <- baseRequest domain Brig Versioned "password-reset"
Expand Down
10 changes: 9 additions & 1 deletion integration/test/Test/Teams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ testInvitePersonalUserToTeam = do
checkListInvitations owner tid email
code <- I.getInvitationCode owner inv >>= getJSON 200 >>= (%. "code") & asString
inv %. "url" & asString >>= assertUrlContainsCode code
bindResponse (getInvitationByCode user code) $ \resp -> do
resp.status `shouldMatchInt` 200
ownersEmail <- owner %. "email" & asString
resp.json %. "created_by_email" `shouldMatch` ownersEmail
acceptTeamInvitation user code Nothing >>= assertStatus 400
acceptTeamInvitation user code (Just "wrong-password") >>= assertStatus 403

Expand Down Expand Up @@ -123,7 +127,11 @@ testInvitePersonalUserToTeam = do
checkListInvitations :: Value -> String -> String -> App ()
checkListInvitations owner tid email = do
newUserEmail <- randomEmail
void $ postInvitation owner (PostInvitation (Just newUserEmail) Nothing) >>= assertSuccess
inv <- postInvitation owner (PostInvitation (Just newUserEmail) Nothing) >>= getJSON 201
code <- I.getInvitationCode owner inv >>= getJSON 200 >>= (%. "code") & asString
bindResponse (getInvitationByCode owner code) $ \resp -> do
resp.status `shouldMatchInt` 200
lookupField resp.json "created_by_email" `shouldMatch` (Nothing :: Maybe Value)
bindResponse (listInvitations owner tid) $ \resp -> do
resp.status `shouldMatchInt` 200
invitations <- resp.json %. "invitations" >>= asList
Expand Down
2 changes: 1 addition & 1 deletion libs/wire-api/src/Wire/API/Routes/Public/Brig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,7 @@ type TeamsAPI =
:> MultiVerb1
'GET
'[JSON]
(Respond 200 "Invitation info" Invitation)
(Respond 200 "Invitation info" InvitationUserView)
)
-- FUTUREWORK: Add another endpoint to allow resending of invitation codes
:<|> Named
Expand Down
68 changes: 43 additions & 25 deletions libs/wire-api/src/Wire/API/Team/Invitation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
module Wire.API.Team.Invitation
( InvitationRequest (..),
Invitation (..),
InvitationUserView (..),
InvitationList (..),
InvitationLocation (..),
AcceptTeamInvitation (..),
Expand Down Expand Up @@ -98,27 +99,31 @@ instance ToSchema Invitation where
schema =
objectWithDocModifier
"Invitation"
(description ?~ "An invitation to join a team on Wire")
$ Invitation
<$> (.team)
.= fieldWithDocModifier "team" (description ?~ "Team ID of the inviting team") schema
<*> (.role)
-- clients, when leaving "role" empty, can leave the default role choice to us
.= (fromMaybe defaultRole <$> optFieldWithDocModifier "role" (description ?~ "Role of the invited user") schema)
<*> (.invitationId)
.= fieldWithDocModifier "id" (description ?~ "UUID used to refer the invitation") schema
<*> (.createdAt)
.= fieldWithDocModifier "created_at" (description ?~ "Timestamp of invitation creation") schema
<*> (.createdBy)
.= optFieldWithDocModifier "created_by" (description ?~ "ID of the inviting user") (maybeWithDefault A.Null schema)
<*> (.inviteeEmail)
.= fieldWithDocModifier "email" (description ?~ "Email of the invitee") schema
<*> (.inviteeName)
.= optFieldWithDocModifier "name" (description ?~ "Name of the invitee (1 - 128 characters)") (maybeWithDefault A.Null schema)
<*> (fmap (TE.decodeUtf8 . serializeURIRef') . inviteeUrl)
.= optFieldWithDocModifier "url" (description ?~ "URL of the invitation link to be sent to the invitee") (maybeWithDefault A.Null urlSchema)
where
urlSchema = parsedText "URIRef_Absolute" (runParser (uriParser strictURIParserOptions) . TE.encodeUtf8)
(description ?~ "An invitation to join a team on Wire. If invitee is invited from an existing personal account, inviter email is included.")
invitationObjectSchema

invitationObjectSchema :: ObjectSchema SwaggerDoc Invitation
invitationObjectSchema =
Invitation
<$> (.team)
.= fieldWithDocModifier "team" (description ?~ "Team ID of the inviting team") schema
<*> (.role)
-- clients, when leaving "role" empty, can leave the default role choice to us
.= (fromMaybe defaultRole <$> optFieldWithDocModifier "role" (description ?~ "Role of the invited user") schema)
<*> (.invitationId)
.= fieldWithDocModifier "id" (description ?~ "UUID used to refer the invitation") schema
<*> (.createdAt)
.= fieldWithDocModifier "created_at" (description ?~ "Timestamp of invitation creation") schema
<*> (.createdBy)
.= optFieldWithDocModifier "created_by" (description ?~ "ID of the inviting user") (maybeWithDefault A.Null schema)
<*> (.inviteeEmail)
.= fieldWithDocModifier "email" (description ?~ "Email of the invitee") schema
<*> (.inviteeName)
.= optFieldWithDocModifier "name" (description ?~ "Name of the invitee (1 - 128 characters)") (maybeWithDefault A.Null schema)
<*> (fmap (TE.decodeUtf8 . serializeURIRef') . (.inviteeUrl))
.= optFieldWithDocModifier "url" (description ?~ "URL of the invitation link to be sent to the invitee") (maybeWithDefault A.Null urlSchema)
where
urlSchema = parsedText "URIRef_Absolute" (runParser (uriParser strictURIParserOptions) . TE.encodeUtf8)

newtype InvitationLocation = InvitationLocation
{ unInvitationLocation :: ByteString
Expand Down Expand Up @@ -175,10 +180,8 @@ instance ToSchema InvitationList where
schema =
objectWithDocModifier "InvitationList" (description ?~ "A list of sent team invitations.") $
InvitationList
<$> ilInvitations
.= field "invitations" (array schema)
<*> ilHasMore
.= fieldWithDocModifier "has_more" (description ?~ "Indicator that the server has more invitations than returned.") schema
<$> ilInvitations .= field "invitations" (array schema)
<*> ilHasMore .= fieldWithDocModifier "has_more" (description ?~ "Indicator that the server has more invitations than returned.") schema

--------------------------------------------------------------------------------
-- AcceptTeamInvitation
Expand All @@ -196,3 +199,18 @@ instance ToSchema AcceptTeamInvitation where
AcceptTeamInvitation
<$> code .= fieldWithDocModifier "code" (description ?~ "Invitation code to accept.") schema
<*> password .= fieldWithDocModifier "password" (description ?~ "The user account password.") schema

data InvitationUserView = InvitationUserView
{ invitation :: Invitation,
inviterEmail :: Maybe EmailAddress
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform InvitationUserView)
deriving (A.FromJSON, A.ToJSON, S.ToSchema) via (Schema InvitationUserView)

instance ToSchema InvitationUserView where
schema =
object "InvitationUserView" $
InvitationUserView
<$> invitation .= invitationObjectSchema
<*> inviterEmail .= maybe_ (optField "created_by_email" schema)
23 changes: 22 additions & 1 deletion libs/wire-api/test/golden/Test/Wire/API/Golden/Generated.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,28 @@ tests =
testGroup "Golden: InvitationRequest_team" $
testObjects [(Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_1, "testObject_InvitationRequest_team_1.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_2, "testObject_InvitationRequest_team_2.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_3, "testObject_InvitationRequest_team_3.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_4, "testObject_InvitationRequest_team_4.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_5, "testObject_InvitationRequest_team_5.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_6, "testObject_InvitationRequest_team_6.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_7, "testObject_InvitationRequest_team_7.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_8, "testObject_InvitationRequest_team_8.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_9, "testObject_InvitationRequest_team_9.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_10, "testObject_InvitationRequest_team_10.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_11, "testObject_InvitationRequest_team_11.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_12, "testObject_InvitationRequest_team_12.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_13, "testObject_InvitationRequest_team_13.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_14, "testObject_InvitationRequest_team_14.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_15, "testObject_InvitationRequest_team_15.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_16, "testObject_InvitationRequest_team_16.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_17, "testObject_InvitationRequest_team_17.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_18, "testObject_InvitationRequest_team_18.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_19, "testObject_InvitationRequest_team_19.json"), (Test.Wire.API.Golden.Generated.InvitationRequest_team.testObject_InvitationRequest_team_20, "testObject_InvitationRequest_team_20.json")],
testGroup "Golden: Invitation_team" $
testObjects [(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_1, "testObject_Invitation_team_1.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_2, "testObject_Invitation_team_2.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_3, "testObject_Invitation_team_3.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_4, "testObject_Invitation_team_4.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_5, "testObject_Invitation_team_5.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_6, "testObject_Invitation_team_6.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_7, "testObject_Invitation_team_7.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_8, "testObject_Invitation_team_8.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_9, "testObject_Invitation_team_9.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_10, "testObject_Invitation_team_10.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_11, "testObject_Invitation_team_11.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_12, "testObject_Invitation_team_12.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_13, "testObject_Invitation_team_13.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_14, "testObject_Invitation_team_14.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_15, "testObject_Invitation_team_15.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_16, "testObject_Invitation_team_16.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_17, "testObject_Invitation_team_17.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_18, "testObject_Invitation_team_18.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_19, "testObject_Invitation_team_19.json"), (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_20, "testObject_Invitation_team_20.json")],
testObjects
[ (Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_1, "testObject_Invitation_team_1.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_2, "testObject_Invitation_team_2.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_3, "testObject_Invitation_team_3.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_4, "testObject_Invitation_team_4.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_5, "testObject_Invitation_team_5.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_6, "testObject_Invitation_team_6.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_7, "testObject_Invitation_team_7.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_8, "testObject_Invitation_team_8.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_9, "testObject_Invitation_team_9.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_10, "testObject_Invitation_team_10.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_11, "testObject_Invitation_team_11.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_12, "testObject_Invitation_team_12.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_13, "testObject_Invitation_team_13.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_14, "testObject_Invitation_team_14.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_15, "testObject_Invitation_team_15.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_16, "testObject_Invitation_team_16.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_17, "testObject_Invitation_team_17.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_18, "testObject_Invitation_team_18.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_19, "testObject_Invitation_team_19.json"),
(Test.Wire.API.Golden.Generated.Invitation_team.testObject_Invitation_team_20, "testObject_Invitation_team_20.json")
],
testGroup "Golden: InvitationList_team" $
testObjects [(Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_1, "testObject_InvitationList_team_1.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_2, "testObject_InvitationList_team_2.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_3, "testObject_InvitationList_team_3.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_4, "testObject_InvitationList_team_4.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_5, "testObject_InvitationList_team_5.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_6, "testObject_InvitationList_team_6.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_7, "testObject_InvitationList_team_7.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_8, "testObject_InvitationList_team_8.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_9, "testObject_InvitationList_team_9.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_10, "testObject_InvitationList_team_10.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_11, "testObject_InvitationList_team_11.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_12, "testObject_InvitationList_team_12.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_13, "testObject_InvitationList_team_13.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_14, "testObject_InvitationList_team_14.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_15, "testObject_InvitationList_team_15.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_16, "testObject_InvitationList_team_16.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_17, "testObject_InvitationList_team_17.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_18, "testObject_InvitationList_team_18.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_19, "testObject_InvitationList_team_19.json"), (Test.Wire.API.Golden.Generated.InvitationList_team.testObject_InvitationList_team_20, "testObject_InvitationList_team_20.json")],
testGroup "Golden: NewLegalHoldService_team" $
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Test.Wire.API.Golden.Generated.Invitation_team where
import Data.Id (Id (Id))
import Data.Json.Util (readUTCTimeMillis)
import Data.UUID qualified as UUID (fromString)
import Imports (Maybe (Just, Nothing), fromJust)
import Imports
import Wire.API.Team.Invitation (Invitation (..))
import Wire.API.Team.Role (Role (RoleAdmin, RoleExternalPartner, RoleMember, RoleOwner))
import Wire.API.User.Identity
Expand Down
6 changes: 6 additions & 0 deletions libs/wire-api/test/golden/Test/Wire/API/Golden/Manual.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import Test.Wire.API.Golden.Manual.FederationRestriction
import Test.Wire.API.Golden.Manual.FederationStatus
import Test.Wire.API.Golden.Manual.GetPaginatedConversationIds
import Test.Wire.API.Golden.Manual.GroupId
import Test.Wire.API.Golden.Manual.InvitationUserView
import Test.Wire.API.Golden.Manual.ListConversations
import Test.Wire.API.Golden.Manual.ListUsersById
import Test.Wire.API.Golden.Manual.LoginId_user
Expand Down Expand Up @@ -311,5 +312,10 @@ tests =
(testObject_Activate_user_2, "testObject_Activate_user_2.json"),
(testObject_Activate_user_3, "testObject_Activate_user_3.json"),
(testObject_Activate_user_4, "testObject_Activate_user_4.json")
],
testGroup "InvitationUserView" $
testObjects
[ (testObject_InvitationUserView_team_1, "testObject_InvitationUserView_team_1.json"),
(testObject_InvitationUserView_team_2, "testObject_InvitationUserView_team_2.json")
]
]
Loading

0 comments on commit 97cf75c

Please sign in to comment.