Skip to content

Commit 0fedcab

Browse files
committed
[public-api] UsRefactor to use conenct handlers
1 parent bcbb27d commit 0fedcab

File tree

7 files changed

+70
-167
lines changed

7 files changed

+70
-167
lines changed

components/public-api-server/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/gitpod-io/gitpod/public-api-server
22

3-
go 1.19
3+
go 1.18
44

55
require (
66
github.com/AdaLogics/go-fuzz-headers v0.0.0-20220708163326-82d177caec6e
@@ -11,8 +11,10 @@ require (
1111
github.com/gitpod-io/gitpod/usage-api v0.0.0-00010101000000-000000000000
1212
github.com/golang/mock v1.6.0
1313
github.com/google/go-cmp v0.5.9
14+
github.com/google/uuid v1.1.2
1415
github.com/gorilla/handlers v1.5.1
1516
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
17+
github.com/mattbarton/go-cookie-signature v0.0.0-20151013134203-95ce9e8d2367
1618
github.com/prometheus/client_golang v1.13.0
1719
github.com/relvacode/iso8601 v1.1.0
1820
github.com/sirupsen/logrus v1.8.1

components/public-api-server/go.sum

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 22 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,59 +7,49 @@ package apiv1
77
import (
88
"context"
99

10+
connect "github.com/bufbuild/connect-go"
1011
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
12+
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
1113
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
1214
v1 "github.com/gitpod-io/gitpod/public-api/v1"
15+
"github.com/gitpod-io/gitpod/public-api/v1/v1connect"
1316
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
1417
"github.com/relvacode/iso8601"
1518
"google.golang.org/grpc"
1619
"google.golang.org/grpc/codes"
17-
"google.golang.org/grpc/metadata"
1820
"google.golang.org/grpc/status"
1921
"google.golang.org/protobuf/types/known/timestamppb"
2022
)
2123

2224
func NewWorkspaceService(serverConnPool proxy.ServerConnectionPool) *WorkspaceService {
2325
return &WorkspaceService{
24-
connectionPool: serverConnPool,
25-
UnimplementedWorkspacesServiceServer: &v1.UnimplementedWorkspacesServiceServer{},
26+
connectionPool: serverConnPool,
2627
}
2728
}
2829

2930
type WorkspaceService struct {
3031
connectionPool proxy.ServerConnectionPool
3132

32-
*v1.UnimplementedWorkspacesServiceServer
33+
v1connect.WorkspacesServiceHandler
3334
}
3435

35-
func (w *WorkspaceService) GetWorkspace(ctx context.Context, r *v1.GetWorkspaceRequest) (*v1.GetWorkspaceResponse, error) {
36+
func (s *WorkspaceService) GetWorkspace(ctx context.Context, req *connect.Request[v1.GetWorkspaceRequest]) (*connect.Response[v1.GetWorkspaceResponse], error) {
37+
token := auth.TokenFromContext(ctx)
3638
logger := ctxlogrus.Extract(ctx)
37-
token, err := bearerTokenFromContext(ctx)
38-
if err != nil {
39-
return nil, err
40-
}
4139

42-
server, err := w.connectionPool.Get(ctx, token)
40+
server, err := s.connectionPool.Get(ctx, token)
4341
if err != nil {
4442
logger.WithError(err).Error("Failed to get connection to server.")
45-
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
43+
return nil, connect.NewError(connect.CodeInternal, err)
4644
}
4745

48-
workspace, err := server.GetWorkspace(ctx, r.GetWorkspaceId())
46+
workspace, err := server.GetWorkspace(ctx, req.Msg.GetWorkspaceId())
4947
if err != nil {
5048
logger.WithError(err).Error("Failed to get workspace.")
51-
converted := proxy.ConvertError(err)
52-
switch status.Code(converted) {
53-
case codes.PermissionDenied:
54-
return nil, status.Error(codes.PermissionDenied, "insufficient permission to access workspace")
55-
case codes.NotFound:
56-
return nil, status.Error(codes.NotFound, "workspace does not exist")
57-
default:
58-
return nil, status.Error(codes.Internal, "unable to retrieve workspace")
59-
}
49+
return nil, proxy.ConvertError(err)
6050
}
6151

62-
return &v1.GetWorkspaceResponse{
52+
return connect.NewResponse(&v1.GetWorkspaceResponse{
6353
Result: &v1.Workspace{
6454
WorkspaceId: workspace.Workspace.ID,
6555
OwnerId: workspace.Workspace.OwnerID,
@@ -73,72 +63,20 @@ func (w *WorkspaceService) GetWorkspace(ctx context.Context, r *v1.GetWorkspaceR
7363
},
7464
Description: workspace.Workspace.Description,
7565
},
76-
}, nil
77-
}
78-
79-
func (w *WorkspaceService) GetOwnerToken(ctx context.Context, r *v1.GetOwnerTokenRequest) (*v1.GetOwnerTokenResponse, error) {
80-
logger := ctxlogrus.Extract(ctx)
81-
token, err := bearerTokenFromContext(ctx)
82-
if err != nil {
83-
return nil, err
84-
}
85-
86-
server, err := w.connectionPool.Get(ctx, token)
87-
if err != nil {
88-
logger.WithError(err).Error("Failed to get connection to server.")
89-
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
90-
}
91-
92-
ownerToken, err := server.GetOwnerToken(ctx, r.GetWorkspaceId())
93-
94-
if err != nil {
95-
logger.WithError(err).Error("Failed to get owner token.")
96-
converted := proxy.ConvertError(err)
97-
switch status.Code(converted) {
98-
case codes.PermissionDenied:
99-
return nil, status.Error(codes.PermissionDenied, "insufficient permission to retrieve ownertoken")
100-
case codes.NotFound:
101-
return nil, status.Error(codes.NotFound, "workspace does not exist")
102-
default:
103-
return nil, status.Error(codes.Internal, "unable to retrieve owner token")
104-
}
105-
}
106-
107-
return &v1.GetOwnerTokenResponse{Token: ownerToken}, nil
66+
}), nil
10867
}
10968

110-
func bearerTokenFromContext(ctx context.Context) (string, error) {
111-
md, ok := metadata.FromIncomingContext(ctx)
112-
if !ok {
113-
return "", status.Error(codes.Unauthenticated, "no credentials provided")
114-
}
115-
116-
values := md.Get("authorization")
117-
if len(values) == 0 {
118-
return "", status.Error(codes.Unauthenticated, "no authorization header specified")
119-
}
120-
if len(values) > 1 {
121-
return "", status.Error(codes.Unauthenticated, "more than one authorization header specified, exactly one is required")
122-
}
123-
124-
token := values[0]
125-
return token, nil
126-
}
127-
128-
func (w *WorkspaceService) ListWorkspaces(ctx context.Context, req *v1.ListWorkspacesRequest) (*v1.ListWorkspacesResponse, error) {
69+
func (s *WorkspaceService) ListWorkspaces(ctx context.Context, req *connect.Request[v1.ListWorkspacesRequest]) (*connect.Response[v1.ListWorkspacesResponse], error) {
12970
logger := ctxlogrus.Extract(ctx)
130-
token, err := bearerTokenFromContext(ctx)
131-
if err != nil {
132-
return nil, err
133-
}
71+
token := auth.TokenFromContext(ctx)
13472

135-
server, err := w.connectionPool.Get(ctx, token)
73+
server, err := s.connectionPool.Get(ctx, token)
13674
if err != nil {
13775
logger.WithError(err).Error("Failed to get connection to server.")
13876
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
13977
}
14078

141-
limit, err := getLimitFromPagination(req.Pagination)
79+
limit, err := getLimitFromPagination(req.Msg.GetPagination())
14280
if err != nil {
14381
// getLimitFromPagination returns gRPC errors
14482
return nil, err
@@ -161,9 +99,11 @@ func (w *WorkspaceService) ListWorkspaces(ctx context.Context, req *v1.ListWorks
16199
res = append(res, workspaceAndInstance)
162100
}
163101

164-
return &v1.ListWorkspacesResponse{
165-
Result: res,
166-
}, nil
102+
return connect.NewResponse(
103+
&v1.ListWorkspacesResponse{
104+
Result: res,
105+
},
106+
), nil
167107
}
168108

169109
func getLimitFromPagination(pagination *v1.Pagination) (int, error) {

components/public-api-server/pkg/proxy/errors.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
package proxy
66

77
import (
8-
"google.golang.org/grpc/codes"
9-
"google.golang.org/grpc/status"
108
"strings"
9+
10+
"github.com/bufbuild/connect-go"
1111
)
1212

1313
func ConvertError(err error) error {
@@ -18,23 +18,23 @@ func ConvertError(err error) error {
1818
s := err.Error()
1919

2020
if strings.Contains(s, "code 401") {
21-
return status.Error(codes.PermissionDenied, s)
21+
return connect.NewError(connect.CodePermissionDenied, err)
2222
}
2323

2424
// components/gitpod-protocol/src/messaging/error.ts
2525
if strings.Contains(s, "code 403") {
26-
return status.Error(codes.PermissionDenied, s)
26+
return connect.NewError(connect.CodePermissionDenied, err)
2727
}
2828

2929
// components/gitpod-protocol/src/messaging/error.ts
3030
if strings.Contains(s, "code 404") {
31-
return status.Error(codes.NotFound, s)
31+
return connect.NewError(connect.CodeNotFound, err)
3232
}
3333

3434
// components/gitpod-messagebus/src/jsonrpc-server.ts#47
3535
if strings.Contains(s, "code -32603") {
36-
return status.Error(codes.Internal, s)
36+
return connect.NewError(connect.CodeInternal, err)
3737
}
3838

39-
return status.Error(codes.Internal, s)
39+
return connect.NewError(connect.CodeInternal, err)
4040
}

components/public-api-server/pkg/proxy/errors_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,32 @@ package proxy
66

77
import (
88
"errors"
9-
"github.com/stretchr/testify/require"
10-
"google.golang.org/grpc/codes"
11-
"google.golang.org/grpc/status"
9+
"fmt"
1210
"testing"
11+
12+
"github.com/bufbuild/connect-go"
13+
"github.com/stretchr/testify/require"
1314
)
1415

1516
func TestConvertError(t *testing.T) {
1617
scenarios := []struct {
1718
WebsocketError error
18-
ExpectedStatus codes.Code
19+
ExpectedStatus connect.Code
1920
}{
2021
{
2122
WebsocketError: errors.New("reconnecting-ws: bad handshake: code 401 - URL: wss://main.preview.gitpod-dev.com/api/v1 - headers: map[Authorization:[Bearer foo] Origin:[http://main.preview.gitpod-dev.com/]]"),
22-
ExpectedStatus: codes.PermissionDenied,
23+
ExpectedStatus: connect.CodePermissionDenied,
2324
},
2425
{
2526
WebsocketError: errors.New("jsonrpc2: code -32603 message: Request getWorkspace failed with message: No workspace with id 'some-id' found."),
26-
ExpectedStatus: codes.Internal,
27+
ExpectedStatus: connect.CodeInternal,
2728
},
2829
}
2930

3031
for _, s := range scenarios {
3132
converted := ConvertError(s.WebsocketError)
32-
require.Equal(t, s.ExpectedStatus, status.Code(converted))
33+
require.Equal(t, s.ExpectedStatus, connect.CodeOf(converted))
3334
// the error message should remain the same
34-
require.Equal(t, s.WebsocketError.Error(), status.Convert(converted).Message())
35+
require.Equal(t, fmt.Errorf("%s: %w", s.ExpectedStatus.String(), s.WebsocketError).Error(), converted.Error())
3536
}
3637
}

components/public-api-server/pkg/server/integration_test.go

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,69 +13,27 @@ import (
1313
"github.com/bufbuild/connect-go"
1414
"github.com/gitpod-io/gitpod/common-go/baseserver"
1515
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
16+
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
17+
"github.com/gitpod-io/gitpod/public-api-server/pkg/webhooks"
1618
v1 "github.com/gitpod-io/gitpod/public-api/v1"
1719
"github.com/gitpod-io/gitpod/public-api/v1/v1connect"
1820
"github.com/stretchr/testify/require"
1921
"google.golang.org/grpc"
2022
"google.golang.org/grpc/codes"
2123
"google.golang.org/grpc/credentials/insecure"
22-
"google.golang.org/grpc/metadata"
2324
"google.golang.org/grpc/status"
2425
)
2526

26-
func TestPublicAPIServer_v1_WorkspaceService(t *testing.T) {
27-
ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "some-token")
28-
srv := baseserver.NewForTests(t,
29-
baseserver.WithGRPC(baseserver.MustUseRandomLocalAddress(t)),
30-
)
31-
32-
gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1")
33-
require.NoError(t, err)
34-
35-
require.NoError(t, register(srv, gitpodAPI))
36-
baseserver.StartServerForTests(t, srv)
37-
38-
conn, err := grpc.Dial(srv.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()))
39-
require.NoError(t, err)
40-
41-
workspaceClient := v1.NewWorkspacesServiceClient(conn)
42-
43-
_, err = workspaceClient.CreateAndStartWorkspace(ctx, &v1.CreateAndStartWorkspaceRequest{})
44-
requireErrorStatusCode(t, codes.Unimplemented, err)
45-
46-
_, err = workspaceClient.StartWorkspace(ctx, &v1.StartWorkspaceRequest{})
47-
requireErrorStatusCode(t, codes.Unimplemented, err)
48-
49-
_, err = workspaceClient.GetActiveWorkspaceInstance(ctx, &v1.GetActiveWorkspaceInstanceRequest{})
50-
requireErrorStatusCode(t, codes.Unimplemented, err)
51-
52-
_, err = workspaceClient.GetWorkspaceInstanceOwnerToken(ctx, &v1.GetWorkspaceInstanceOwnerTokenRequest{})
53-
requireErrorStatusCode(t, codes.Unimplemented, err)
54-
55-
stopWorkspaceStream, err := workspaceClient.StopWorkspace(ctx, &v1.StopWorkspaceRequest{})
56-
require.NoError(t, err)
57-
_, err = stopWorkspaceStream.Recv()
58-
requireErrorStatusCode(t, codes.Unimplemented, err)
59-
60-
listenWorkspaceStream, err := workspaceClient.ListenToWorkspaceInstance(ctx, &v1.ListenToWorkspaceInstanceRequest{})
61-
require.NoError(t, err)
62-
_, err = listenWorkspaceStream.Recv()
63-
requireErrorStatusCode(t, codes.Unimplemented, err)
64-
65-
listenImageBuildStream, err := workspaceClient.ListenToImageBuildLogs(ctx, &v1.ListenToImageBuildLogsRequest{})
66-
require.NoError(t, err)
67-
_, err = listenImageBuildStream.Recv()
68-
requireErrorStatusCode(t, codes.Unimplemented, err)
69-
}
70-
7127
func TestPublicAPIServer_v1_PrebuildService(t *testing.T) {
7228
ctx := context.Background()
7329
srv := baseserver.NewForTests(t, baseserver.WithGRPC(baseserver.MustUseRandomLocalAddress(t)))
7430

7531
gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1")
7632
require.NoError(t, err)
7733

78-
require.NoError(t, register(srv, gitpodAPI))
34+
connPool := &proxy.NoConnectionPool{ServerAPI: gitpodAPI}
35+
36+
require.NoError(t, register(srv, connPool, webhooks.NewNoopWebhookHandler()))
7937

8038
baseserver.StartServerForTests(t, srv)
8139

@@ -111,7 +69,9 @@ func TestPublicAPIServer_WorkspaceServiceHandler(t *testing.T) {
11169
gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1")
11270
require.NoError(t, err)
11371

114-
require.NoError(t, register(srv, gitpodAPI))
72+
connPool := &proxy.NoConnectionPool{ServerAPI: gitpodAPI}
73+
74+
require.NoError(t, register(srv, connPool, webhooks.NewNoopWebhookHandler()))
11575
baseserver.StartServerForTests(t, srv)
11676

11777
client := v1connect.NewWorkspacesServiceClient(http.DefaultClient, srv.HTTPAddress())

0 commit comments

Comments
 (0)