mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
299 lines
8.5 KiB
Go
299 lines
8.5 KiB
Go
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package apiv1
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
connect "github.com/bufbuild/connect-go"
|
|
"github.com/gitpod-io/gitpod/common-go/log"
|
|
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
|
|
"github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
|
|
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
|
|
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
|
|
)
|
|
|
|
func NewTeamsService(pool proxy.ServerConnectionPool) *TeamService {
|
|
return &TeamService{
|
|
connectionPool: pool,
|
|
}
|
|
}
|
|
|
|
var _ v1connect.TeamsServiceHandler = (*TeamService)(nil)
|
|
|
|
type TeamService struct {
|
|
connectionPool proxy.ServerConnectionPool
|
|
|
|
v1connect.UnimplementedTeamsServiceHandler
|
|
}
|
|
|
|
func (s *TeamService) CreateTeam(ctx context.Context, req *connect.Request[v1.CreateTeamRequest]) (*connect.Response[v1.CreateTeamResponse], error) {
|
|
if req.Msg.GetName() == "" {
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Name is a required argument when creating a team."))
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
created, err := conn.CreateTeam(ctx, req.Msg.GetName())
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to create team.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
team, err := s.toTeamAPIResponse(ctx, conn, created)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to populate team with details.")
|
|
return nil, err
|
|
}
|
|
|
|
return connect.NewResponse(&v1.CreateTeamResponse{
|
|
Team: team,
|
|
}), nil
|
|
}
|
|
|
|
func (s *TeamService) GetTeam(ctx context.Context, req *connect.Request[v1.GetTeamRequest]) (*connect.Response[v1.GetTeamResponse], error) {
|
|
teamID, err := validateTeamID(req.Msg.GetTeamId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
team, err := conn.GetTeam(ctx, teamID.String())
|
|
if err != nil {
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
response, err := s.toTeamAPIResponse(ctx, conn, team)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return connect.NewResponse(&v1.GetTeamResponse{
|
|
Team: response,
|
|
}), nil
|
|
}
|
|
|
|
func (s *TeamService) ListTeams(ctx context.Context, req *connect.Request[v1.ListTeamsRequest]) (*connect.Response[v1.ListTeamsResponse], error) {
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
teams, err := conn.GetTeams(ctx)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to list teams from server.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
var response []*v1.Team
|
|
for _, t := range teams {
|
|
team, err := s.toTeamAPIResponse(ctx, conn, t)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to populate team with details.")
|
|
return nil, err
|
|
}
|
|
|
|
response = append(response, team)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.ListTeamsResponse{
|
|
Teams: response,
|
|
}), nil
|
|
}
|
|
|
|
func (s *TeamService) DeleteTeam(ctx context.Context, req *connect.Request[v1.DeleteTeamRequest]) (*connect.Response[v1.DeleteTeamResponse], error) {
|
|
teamID, err := validateTeamID(req.Msg.GetTeamId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conn.DeleteTeam(ctx, teamID.String())
|
|
if err != nil {
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.DeleteTeamResponse{}), 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"))
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
team, err := conn.JoinTeam(ctx, req.Msg.GetInvitationId())
|
|
if err != nil {
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
response, err := s.toTeamAPIResponse(ctx, conn, team)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to populate team with details.")
|
|
return nil, err
|
|
}
|
|
|
|
return connect.NewResponse(&v1.JoinTeamResponse{
|
|
Team: response,
|
|
}), nil
|
|
}
|
|
|
|
func (s *TeamService) ResetTeamInvitation(ctx context.Context, req *connect.Request[v1.ResetTeamInvitationRequest]) (*connect.Response[v1.ResetTeamInvitationResponse], error) {
|
|
teamID, err := validateTeamID(req.Msg.GetTeamId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
invite, err := conn.ResetGenericInvite(ctx, teamID.String())
|
|
if err != nil {
|
|
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("Failed to reset team invitation"))
|
|
}
|
|
|
|
return connect.NewResponse(&v1.ResetTeamInvitationResponse{
|
|
TeamInvitation: teamInviteToAPIResponse(invite),
|
|
}), nil
|
|
}
|
|
|
|
func (s *TeamService) UpdateTeamMember(ctx context.Context, req *connect.Request[v1.UpdateTeamMemberRequest]) (*connect.Response[v1.UpdateTeamMemberResponse], error) {
|
|
teamID := req.Msg.GetTeamId()
|
|
if teamID == "" {
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Team ID is a required parameter to update team member."))
|
|
}
|
|
|
|
userID := req.Msg.GetTeamMember().GetUserId()
|
|
if userID == "" {
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("TeamMember.UserID is a required parameter to update team member."))
|
|
}
|
|
|
|
var role protocol.TeamMemberRole
|
|
switch req.Msg.GetTeamMember().GetRole() {
|
|
case v1.TeamRole_TEAM_ROLE_MEMBER:
|
|
role = protocol.TeamMember_Member
|
|
case v1.TeamRole_TEAM_ROLE_OWNER:
|
|
role = protocol.TeamMember_Owner
|
|
default:
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Unknown TeamMember.Role specified."))
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conn.SetTeamMemberRole(ctx, teamID, userID, role)
|
|
if err != nil {
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.UpdateTeamMemberResponse{
|
|
TeamMember: req.Msg.GetTeamMember(),
|
|
}), nil
|
|
|
|
}
|
|
|
|
func (s *TeamService) DeleteTeamMember(ctx context.Context, req *connect.Request[v1.DeleteTeamMemberRequest]) (*connect.Response[v1.DeleteTeamMemberResponse], error) {
|
|
teamID := req.Msg.GetTeamId()
|
|
if teamID == "" {
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Team ID is a required parameter to delete team member."))
|
|
}
|
|
|
|
memberID := req.Msg.GetTeamMemberId()
|
|
if memberID == "" {
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Team Member ID is a required parameter to delete team member."))
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conn.RemoveTeamMember(ctx, teamID, memberID)
|
|
if err != nil {
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.DeleteTeamMemberResponse{}), nil
|
|
}
|
|
|
|
func (s *TeamService) toTeamAPIResponse(ctx context.Context, conn protocol.APIInterface, team *protocol.Team) (*v1.Team, error) {
|
|
members, err := conn.GetTeamMembers(ctx, team.ID)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to get team members.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
invite, err := conn.GetGenericInvite(ctx, team.ID)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to get generic invite.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return teamToAPIResponse(team, members, invite), nil
|
|
}
|
|
|
|
func teamToAPIResponse(team *protocol.Team, members []*protocol.TeamMemberInfo, invite *protocol.TeamMembershipInvite) *v1.Team {
|
|
return &v1.Team{
|
|
Id: team.ID,
|
|
Name: team.Name,
|
|
Members: teamMembersToAPIResponse(members),
|
|
TeamInvitation: teamInviteToAPIResponse(invite),
|
|
}
|
|
}
|
|
|
|
func teamMembersToAPIResponse(members []*protocol.TeamMemberInfo) []*v1.TeamMember {
|
|
var result []*v1.TeamMember
|
|
|
|
for _, m := range members {
|
|
result = append(result, &v1.TeamMember{
|
|
UserId: m.UserId,
|
|
Role: teamRoleToAPIResponse(m.Role),
|
|
MemberSince: parseGitpodTimeStampOrDefault(m.MemberSince),
|
|
AvatarUrl: m.AvatarUrl,
|
|
FullName: m.FullName,
|
|
PrimaryEmail: m.PrimaryEmail,
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func teamRoleToAPIResponse(role protocol.TeamMemberRole) v1.TeamRole {
|
|
switch role {
|
|
case protocol.TeamMember_Owner:
|
|
return v1.TeamRole_TEAM_ROLE_OWNER
|
|
case protocol.TeamMember_Member:
|
|
return v1.TeamRole_TEAM_ROLE_MEMBER
|
|
default:
|
|
return v1.TeamRole_TEAM_ROLE_UNSPECIFIED
|
|
}
|
|
}
|
|
|
|
func teamInviteToAPIResponse(invite *protocol.TeamMembershipInvite) *v1.TeamInvitation {
|
|
return &v1.TeamInvitation{
|
|
Id: invite.ID,
|
|
}
|
|
}
|