Skip to content

Commit e1b7d59

Browse files
committed
[public-api] Implement experimental ListTeams
1 parent 4c3007a commit e1b7d59

File tree

4 files changed

+160
-21
lines changed

4 files changed

+160
-21
lines changed

components/public-api-server/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/google/uuid v1.1.2
1515
github.com/gorilla/handlers v1.5.1
1616
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
17+
github.com/hashicorp/golang-lru v0.5.4
1718
github.com/prometheus/client_golang v1.13.0
1819
github.com/relvacode/iso8601 v1.1.0
1920
github.com/sirupsen/logrus v1.8.1
@@ -34,7 +35,6 @@ require (
3435
github.com/golang/protobuf v1.5.2 // indirect
3536
github.com/gorilla/websocket v1.5.0 // indirect
3637
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
37-
github.com/hashicorp/golang-lru v0.5.4 // indirect
3838
github.com/heptiolabs/healthcheck v0.0.0-20211123025425-613501dd5deb // indirect
3939
github.com/inconshreveable/mousetrap v1.0.0 // indirect
4040
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect

components/public-api-server/go.sum

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/public-api-server/pkg/apiv1/team.go

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ package apiv1
77
import (
88
"context"
99
"fmt"
10+
1011
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
11-
1212
connect "github.com/bufbuild/connect-go"
1313
"github.com/gitpod-io/gitpod/common-go/log"
1414
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
@@ -38,38 +38,82 @@ func (s *TeamService) CreateTeam(ctx context.Context, req *connect.Request[v1.Cr
3838
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Name is a required argument when creating a team."))
3939
}
4040

41-
server, err := s.connectionPool.Get(ctx, token)
41+
conn, err := s.connectionPool.Get(ctx, token)
4242
if err != nil {
4343
log.Log.WithError(err).Error("Failed to get connection to server.")
4444
return nil, connect.NewError(connect.CodeInternal, err)
4545
}
4646

47-
team, err := server.CreateTeam(ctx, req.Msg.GetName())
47+
created, err := conn.CreateTeam(ctx, req.Msg.GetName())
4848
if err != nil {
49+
log.WithError(err).Error("Failed to create team.")
4950
return nil, proxy.ConvertError(err)
5051
}
5152

52-
members, err := server.GetTeamMembers(ctx, team.ID)
53+
team, err := s.toTeamAPIResponse(ctx, conn, created)
54+
if err != nil {
55+
log.WithError(err).Error("Failed to populate team with details.")
56+
return nil, err
57+
}
58+
59+
return connect.NewResponse(&v1.CreateTeamResponse{
60+
Team: team,
61+
}), nil
62+
}
63+
64+
func (s *TeamService) ListTeams(ctx context.Context, req *connect.Request[v1.ListTeamsRequest]) (*connect.Response[v1.ListTeamsResponse], error) {
65+
token := auth.TokenFromContext(ctx)
66+
67+
conn, err := s.connectionPool.Get(ctx, token)
5368
if err != nil {
69+
log.Log.WithError(err).Error("Failed to get connection to server.")
70+
return nil, connect.NewError(connect.CodeInternal, err)
71+
}
72+
73+
teams, err := conn.GetTeams(ctx)
74+
if err != nil {
75+
log.WithError(err).Error("Failed to list teams from server.")
5476
return nil, proxy.ConvertError(err)
5577
}
5678

57-
invite, err := server.GetGenericInvite(ctx, team.ID)
79+
var response []*v1.Team
80+
for _, t := range teams {
81+
team, err := s.toTeamAPIResponse(ctx, conn, t)
82+
if err != nil {
83+
log.WithError(err).Error("Failed to populate team with details.")
84+
return nil, err
85+
}
86+
87+
response = append(response, team)
88+
}
89+
90+
return connect.NewResponse(&v1.ListTeamsResponse{
91+
Teams: response,
92+
}), nil
93+
}
94+
95+
func (s *TeamService) toTeamAPIResponse(ctx context.Context, conn protocol.APIInterface, team *protocol.Team) (*v1.Team, error) {
96+
members, err := conn.GetTeamMembers(ctx, team.ID)
5897
if err != nil {
98+
log.WithError(err).Error("Failed to get team members.")
5999
return nil, proxy.ConvertError(err)
60100
}
61101

62-
return connect.NewResponse(&v1.CreateTeamResponse{
63-
Team: &v1.Team{
64-
Id: team.ID,
65-
Name: team.Name,
66-
Slug: team.Slug,
67-
Members: teamMembersToAPIResponse(members),
68-
TeamInvitation: &v1.TeamInvitation{
69-
Id: invite.ID,
70-
},
102+
invite, err := conn.GetGenericInvite(ctx, team.ID)
103+
if err != nil {
104+
log.WithError(err).Error("Failed to get generic invite.")
105+
return nil, proxy.ConvertError(err)
106+
}
107+
108+
return &v1.Team{
109+
Id: team.ID,
110+
Name: team.Name,
111+
Slug: team.Slug,
112+
Members: teamMembersToAPIResponse(members),
113+
TeamInvitation: &v1.TeamInvitation{
114+
Id: invite.ID,
71115
},
72-
}), nil
116+
}, nil
73117
}
74118

75119
func teamMembersToAPIResponse(members []*protocol.TeamMemberInfo) []*v1.TeamMember {

components/public-api-server/pkg/apiv1/team_test.go

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ package apiv1
77
import (
88
"context"
99
"errors"
10+
"net/http"
11+
"net/http/httptest"
12+
"testing"
13+
"time"
14+
1015
"github.com/bufbuild/connect-go"
1116
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
1217
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
@@ -17,10 +22,6 @@ import (
1722
"github.com/google/uuid"
1823
"github.com/stretchr/testify/require"
1924
"google.golang.org/protobuf/testing/protocmp"
20-
"net/http"
21-
"net/http/httptest"
22-
"testing"
23-
"time"
2425
)
2526

2627
func TestTeamsService_CreateTeam(t *testing.T) {
@@ -116,6 +117,101 @@ func TestTeamsService_CreateTeam(t *testing.T) {
116117
})
117118
}
118119

120+
func TestTeamsService_ListTeams(t *testing.T) {
121+
t.Run("returns teams with members and invite", func(t *testing.T) {
122+
ctx := context.Background()
123+
serverMock, client := setupTeamService(t)
124+
125+
teamMembers := []*protocol.TeamMemberInfo{
126+
{
127+
UserId: uuid.New().String(),
128+
FullName: "Alice Alice",
129+
PrimaryEmail: "[email protected]",
130+
AvatarUrl: "",
131+
Role: protocol.TeamMember_Owner,
132+
MemberSince: "",
133+
}, {
134+
UserId: uuid.New().String(),
135+
FullName: "Bob Bob",
136+
PrimaryEmail: "[email protected]",
137+
AvatarUrl: "",
138+
Role: protocol.TeamMember_Member,
139+
MemberSince: "",
140+
},
141+
}
142+
teams := []*protocol.Team{
143+
{
144+
ID: uuid.New().String(),
145+
Name: "Team A",
146+
Slug: "team_a",
147+
}, {
148+
ID: uuid.New().String(),
149+
Name: "Team B",
150+
Slug: "team_ba",
151+
},
152+
}
153+
inviteID := uuid.New().String()
154+
155+
serverMock.EXPECT().GetTeams(gomock.Any()).Return(teams, nil)
156+
157+
// Mocks for populating team A details
158+
serverMock.EXPECT().GetTeamMembers(gomock.Any(), teams[0].ID).Return(teamMembers, nil)
159+
serverMock.EXPECT().GetGenericInvite(gomock.Any(), teams[0].ID).Return(&protocol.TeamMembershipInvite{
160+
ID: inviteID,
161+
TeamID: teams[0].ID,
162+
}, nil)
163+
// Mock for populating team B details
164+
serverMock.EXPECT().GetTeamMembers(gomock.Any(), teams[1].ID).Return(teamMembers, nil)
165+
serverMock.EXPECT().GetGenericInvite(gomock.Any(), teams[1].ID).Return(&protocol.TeamMembershipInvite{
166+
ID: inviteID,
167+
TeamID: teams[1].ID,
168+
}, nil)
169+
170+
response, err := client.ListTeams(ctx, connect.NewRequest(&v1.ListTeamsRequest{}))
171+
require.NoError(t, err)
172+
requireEqualProto(t, &v1.ListTeamsResponse{
173+
Teams: []*v1.Team{
174+
{
175+
Id: teams[0].ID,
176+
Name: teams[0].Name,
177+
Slug: teams[0].Slug,
178+
Members: []*v1.TeamMember{
179+
{
180+
UserId: teamMembers[0].UserId,
181+
Role: teamRoleToAPIResponse(teamMembers[0].Role),
182+
},
183+
{
184+
UserId: teamMembers[1].UserId,
185+
Role: teamRoleToAPIResponse(teamMembers[1].Role),
186+
},
187+
},
188+
TeamInvitation: &v1.TeamInvitation{
189+
Id: inviteID,
190+
},
191+
},
192+
{
193+
Id: teams[1].ID,
194+
Name: teams[1].Name,
195+
Slug: teams[1].Slug,
196+
Members: []*v1.TeamMember{
197+
{
198+
UserId: teamMembers[0].UserId,
199+
Role: teamRoleToAPIResponse(teamMembers[0].Role),
200+
},
201+
{
202+
UserId: teamMembers[1].UserId,
203+
Role: teamRoleToAPIResponse(teamMembers[1].Role),
204+
},
205+
},
206+
TeamInvitation: &v1.TeamInvitation{
207+
Id: inviteID,
208+
},
209+
},
210+
},
211+
}, response.Msg)
212+
})
213+
}
214+
119215
func setupTeamService(t *testing.T) (*protocol.MockAPIInterface, v1connect.TeamsServiceClient) {
120216
t.Helper()
121217

0 commit comments

Comments
 (0)