Skip to content

Commit

Permalink
[papi] add ListTeamMembers and GetTeamInvitation APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
mustard-mh committed Oct 13, 2023
1 parent 83d20c1 commit 1e747a8
Show file tree
Hide file tree
Showing 9 changed files with 932 additions and 163 deletions.
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

0 comments on commit 1e747a8

Please sign in to comment.