Skip to content
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

[papi] add ListTeamMembers and GetTeamInvitation APIs #18919

Merged
merged 2 commits into from
Oct 13, 2023
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
42 changes: 42 additions & 0 deletions components/public-api-server/pkg/apiv1/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,27 @@ func (s *TeamService) DeleteTeam(ctx context.Context, req *connect.Request[v1.De
return connect.NewResponse(&v1.DeleteTeamResponse{}), nil
}

func (s *TeamService) GetTeamInvitation(ctx context.Context, req *connect.Request[v1.GetTeamInvitationRequest]) (*connect.Response[v1.GetTeamInvitationResponse], error) {
teamID, err := validateTeamID(ctx, req.Msg.GetTeamId())
if err != nil {
return nil, err
}

conn, err := getConnection(ctx, s.connectionPool)
if err != nil {
return nil, err
}

invite, err := conn.GetGenericInvite(ctx, teamID.String())
if err != nil {
return nil, proxy.ConvertError(err)
}

return connect.NewResponse(&v1.GetTeamInvitationResponse{
TeamInvitation: teamInviteToAPIResponse(invite),
}), nil
}

func (s *TeamService) JoinTeam(ctx context.Context, req *connect.Request[v1.JoinTeamRequest]) (*connect.Response[v1.JoinTeamResponse], error) {
if req.Msg.GetInvitationId() == "" {
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invitation id is a required argument to join a team"))
Expand Down Expand Up @@ -208,6 +229,27 @@ func (s *TeamService) ResetTeamInvitation(ctx context.Context, req *connect.Requ
}), nil
}

func (s *TeamService) ListTeamMembers(ctx context.Context, req *connect.Request[v1.ListTeamMembersRequest]) (*connect.Response[v1.ListTeamMembersResponse], error) {
teamID, err := validateTeamID(ctx, req.Msg.GetTeamId())
if err != nil {
return nil, err
}

conn, err := getConnection(ctx, s.connectionPool)
if err != nil {
return nil, err
}

members, err := conn.GetTeamMembers(ctx, teamID.String())
if err != nil {
return nil, proxy.ConvertError(err)
}

return connect.NewResponse(&v1.ListTeamMembersResponse{
Members: teamMembersToAPIResponse(members),
}), nil
}

func (s *TeamService) UpdateTeamMember(ctx context.Context, req *connect.Request[v1.UpdateTeamMemberRequest]) (*connect.Response[v1.UpdateTeamMemberResponse], error) {
teamID := req.Msg.GetTeamId()
if teamID == "" {
Expand Down
99 changes: 99 additions & 0 deletions components/public-api-server/pkg/apiv1/team_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,59 @@ func TestTeamToAPIResponse(t *testing.T) {
}, response)
}

func TestTeamsService_ListTeamMembers(t *testing.T) {
t.Run("missing team ID returns invalid argument", func(t *testing.T) {
_, client := setupTeamService(t)

_, err := client.ListTeamMembers(context.Background(), connect.NewRequest(&v1.ListTeamMembersRequest{}))
require.Error(t, err)
require.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err))
})

t.Run("returns permission denied for non-owner", func(t *testing.T) {
ctx := context.Background()
serverMock, client := setupTeamService(t)

team := newTeam(&protocol.Team{
Name: "Team A",
})
serverMock.EXPECT().GetTeamMembers(gomock.Any(), team.ID).Return(nil, &jsonrpc2.Error{Code: 403, Message: "not access"})

_, err := client.ListTeamMembers(ctx, connect.NewRequest(&v1.ListTeamMembersRequest{
TeamId: team.ID,
}))
require.Error(t, err)
require.Equal(t, connect.CodePermissionDenied, connect.CodeOf(err))
})

t.Run("returns members", func(t *testing.T) {
teamMembers := []*protocol.TeamMemberInfo{
newTeamMember(&protocol.TeamMemberInfo{
FullName: "Alice Alice",
Role: protocol.TeamMember_Owner,
}),
newTeamMember(&protocol.TeamMemberInfo{
FullName: "Bob Bob",
Role: protocol.TeamMember_Member,
}),
}
team := newTeam(&protocol.Team{
ID: uuid.New().String(),
})

serverMock, client := setupTeamService(t)

serverMock.EXPECT().GetTeamMembers(gomock.Any(), team.ID).Return(teamMembers, nil)

response, err := client.ListTeamMembers(context.Background(), connect.NewRequest(&v1.ListTeamMembersRequest{TeamId: team.ID}))
require.NoError(t, err)

requireEqualProto(t, &v1.ListTeamMembersResponse{
Members: teamMembersToAPIResponse(teamMembers),
}, response.Msg)
})
}

func TestTeamsService_UpdateTeamMember(t *testing.T) {
var (
teamID = uuid.New().String()
Expand Down Expand Up @@ -476,6 +529,52 @@ func TestTeamService_ResetTeamInvitation(t *testing.T) {
})
}

func TestTeamService_GetTeamInvitation(t *testing.T) {
t.Run("missing team ID returns invalid argument", func(t *testing.T) {
_, client := setupTeamService(t)

_, err := client.GetTeamInvitation(context.Background(), connect.NewRequest(&v1.GetTeamInvitationRequest{}))
require.Error(t, err)
require.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err))
})

t.Run("proxies request to server", func(t *testing.T) {
teamID := uuid.New().String()

serverMock, client := setupTeamService(t)

invite := &protocol.TeamMembershipInvite{
ID: uuid.New().String(),
}

serverMock.EXPECT().GetGenericInvite(gomock.Any(), teamID).Return(invite, nil)

response, err := client.GetTeamInvitation(context.Background(), connect.NewRequest(&v1.GetTeamInvitationRequest{
TeamId: teamID,
}))
require.NoError(t, err)
requireEqualProto(t, &v1.GetTeamInvitationResponse{
TeamInvitation: teamInviteToAPIResponse(invite),
}, response.Msg)
})

t.Run("returns permission denied for non-owner", func(t *testing.T) {
ctx := context.Background()
serverMock, client := setupTeamService(t)

team := newTeam(&protocol.Team{
Name: "Team A",
})
serverMock.EXPECT().GetGenericInvite(gomock.Any(), team.ID).Return(nil, &jsonrpc2.Error{Code: 403, Message: "not access"})

_, err := client.GetTeamInvitation(ctx, connect.NewRequest(&v1.GetTeamInvitationRequest{
TeamId: team.ID,
}))
require.Error(t, err)
require.Equal(t, connect.CodePermissionDenied, connect.CodeOf(err))
})
}

func TestTeamService_DeleteTeam(t *testing.T) {
t.Run("missing team ID returns invalid argument", func(t *testing.T) {
_, client := setupTeamService(t)
Expand Down
25 changes: 25 additions & 0 deletions components/public-api/gitpod/experimental/v1/teams.proto
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,18 @@ service TeamsService {
// DeleteTeam deletes the specified team.
rpc DeleteTeam(DeleteTeamRequest) returns (DeleteTeamResponse) {};

// GetTeamInvitation retrieves the invitation for a Team.
rpc GetTeamInvitation(GetTeamInvitationRequest) returns (GetTeamInvitationResponse) {};

// JoinTeam makes the caller a TeamMember of the Team.
rpc JoinTeam(JoinTeamRequest) returns (JoinTeamResponse) {};

// ResetTeamInvitation resets the invitation_id for a Team.
rpc ResetTeamInvitation(ResetTeamInvitationRequest) returns (ResetTeamInvitationResponse) {};

// ListTeamMembers lists the members of a Team.
rpc ListTeamMembers(ListTeamMembersRequest) returns (ListTeamMembersResponse) {};

// UpdateTeamMember updates team membership properties.
rpc UpdateTeamMember(UpdateTeamMemberRequest) returns (UpdateTeamMemberResponse) {};

Expand Down Expand Up @@ -123,6 +129,15 @@ message DeleteTeamRequest {
message DeleteTeamResponse {
}

message GetTeamInvitationRequest {
string team_id = 1;
}

message GetTeamInvitationResponse {
// team_invitation is the invitation for the team.
TeamInvitation team_invitation = 1;
}

message JoinTeamRequest {
// invitation_id is the invitation ID for a Team
string invitation_id = 1;
Expand All @@ -142,6 +157,16 @@ message ResetTeamInvitationResponse {
TeamInvitation team_invitation = 1;
}

message ListTeamMembersRequest {
// team_id is the ID of the team that contains the members to list
string team_id = 1;
}

message ListTeamMembersResponse {
// members are the team members of this Team
repeated TeamMember members = 1;
}

message UpdateTeamMemberRequest {
// team_id is the ID of the team in which the role is to be updated
string team_id = 1;
Expand Down
Loading