Skip to content

Commit 98e6fe7

Browse files
Laurie T. Malauroboquat
Laurie T. Malau
authored andcommitted
[public api] Use tokens feature flag
1 parent 151f8d5 commit 98e6fe7

File tree

7 files changed

+148
-12
lines changed

7 files changed

+148
-12
lines changed

components/common-go/experiments/flags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ import "context"
1010
func IsMyFirstFeatureFlagEnabled(ctx context.Context, client Client, attributes Attributes) bool {
1111
return client.GetBoolValue(ctx, "isMyFirstFeatureEnabled", false, attributes)
1212
}
13+
14+
func IsPersonalAccessTokensEnabled(ctx context.Context, client Client, attributes Attributes) bool {
15+
return client.GetBoolValue(ctx, "personalAccessTokensEnabled", false, attributes)
16+
}

components/public-api-server/go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ require (
66
github.com/AdaLogics/go-fuzz-headers v0.0.0-20220708163326-82d177caec6e
77
github.com/bufbuild/connect-go v1.0.0
88
github.com/gitpod-io/gitpod/common-go v0.0.0-00010101000000-000000000000
9-
github.com/gitpod-io/gitpod/gitpod-protocol v0.0.0-00010101000000-000000000000
109
github.com/gitpod-io/gitpod/components/public-api/go v0.0.0-00010101000000-000000000000
10+
github.com/gitpod-io/gitpod/gitpod-protocol v0.0.0-00010101000000-000000000000
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
@@ -18,6 +18,7 @@ require (
1818
github.com/prometheus/client_golang v1.13.0
1919
github.com/relvacode/iso8601 v1.1.0
2020
github.com/sirupsen/logrus v1.8.1
21+
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
2122
github.com/spf13/cobra v1.4.0
2223
github.com/stretchr/testify v1.7.0
2324
github.com/stripe/stripe-go/v72 v72.122.0
@@ -27,8 +28,10 @@ require (
2728

2829
require (
2930
github.com/beorn7/perks v1.0.1 // indirect
31+
github.com/blang/semver v3.5.1+incompatible // indirect
3032
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
3133
github.com/cespare/xxhash/v2 v2.1.2 // indirect
34+
github.com/configcat/go-sdk/v7 v7.6.0 // indirect
3235
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
3336
github.com/davecgh/go-spew v1.1.1 // indirect
3437
github.com/felixge/httpsnoop v1.0.1 // indirect
@@ -44,7 +47,6 @@ require (
4447
github.com/prometheus/common v0.37.0 // indirect
4548
github.com/prometheus/procfs v0.8.0 // indirect
4649
github.com/slok/go-http-metrics v0.10.0 // indirect
47-
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 // indirect
4850
github.com/spf13/pflag v1.0.5 // indirect
4951
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
5052
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect

components/public-api-server/go.sum

Lines changed: 7 additions & 0 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/tokens.go

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,68 @@
44

55
package apiv1
66

7-
import "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
7+
import (
8+
"context"
9+
"errors"
10+
"fmt"
811

9-
func NewTokensService() *TokensService {
10-
return &TokensService{}
12+
"github.com/bufbuild/connect-go"
13+
"github.com/gitpod-io/gitpod/common-go/experiments"
14+
"github.com/gitpod-io/gitpod/common-go/log"
15+
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
16+
"github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
17+
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
18+
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
19+
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
20+
)
21+
22+
func NewTokensService(connPool proxy.ServerConnectionPool, expClient experiments.Client) *TokensService {
23+
return &TokensService{
24+
connectionPool: connPool,
25+
expClient: expClient,
26+
}
1127
}
1228

1329
type TokensService struct {
30+
connectionPool proxy.ServerConnectionPool
31+
32+
expClient experiments.Client
33+
1434
v1connect.UnimplementedTokensServiceHandler
1535
}
36+
37+
func (s *TokensService) CreatePersonalAccessToken(ctx context.Context, req *connect.Request[v1.CreatePersonalAccessTokenRequest]) (*connect.Response[v1.CreatePersonalAccessTokenResponse], error) {
38+
39+
conn, err := s.getConnection(ctx)
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
user, err := conn.GetLoggedInUser(ctx)
45+
if err != nil {
46+
return nil, proxy.ConvertError(err)
47+
}
48+
49+
isEnabled := experiments.IsPersonalAccessTokensEnabled(ctx, s.expClient, experiments.Attributes{UserID: user.ID})
50+
51+
if !isEnabled {
52+
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("This feature is currently in beta. If you would like to be part of the beta, please contact us."))
53+
}
54+
55+
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("gitpod.experimental.v1.TokensService.CreatePersonalAccessToken is not implemented"))
56+
}
57+
58+
func (s *TokensService) getConnection(ctx context.Context) (protocol.APIInterface, error) {
59+
token, err := auth.TokenFromContext(ctx)
60+
if err != nil {
61+
return nil, connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("No credentials present on request."))
62+
}
63+
64+
conn, err := s.connectionPool.Get(ctx, token)
65+
if err != nil {
66+
log.Log.WithError(err).Error("Failed to get connection to server.")
67+
return nil, connect.NewError(connect.CodeInternal, errors.New("Failed to establish connection to downstream services. If this issue persists, please contact Gitpod Support."))
68+
}
69+
70+
return conn, nil
71+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License-AGPL.txt in the project root for license information.
4+
5+
package apiv1
6+
7+
import (
8+
"context"
9+
"net/http"
10+
"net/http/httptest"
11+
"testing"
12+
13+
connect "github.com/bufbuild/connect-go"
14+
"github.com/gitpod-io/gitpod/common-go/experiments"
15+
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
16+
"github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
17+
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
18+
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
19+
"github.com/golang/mock/gomock"
20+
"github.com/google/uuid"
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func TestTokensService_CreatePersonalAccessTokenWithoutFeatureFlag(t *testing.T) {
25+
t.Run("returns a personal access token", func(t *testing.T) {
26+
serverMock, client := setupTokensService(t)
27+
28+
user := &protocol.User{
29+
ID: uuid.New().String(),
30+
Name: "Someone",
31+
CreationDate: "2022-11-15T10:10:10.000Z",
32+
}
33+
34+
serverMock.EXPECT().GetLoggedInUser(gomock.Any()).Return(user, nil)
35+
36+
_, err := client.CreatePersonalAccessToken(context.Background(), &connect.Request[v1.CreatePersonalAccessTokenRequest]{})
37+
38+
require.Error(t, err, "This feature is currently in beta. If you would like to be part of the beta, please contact us.")
39+
require.Equal(t, connect.CodePermissionDenied, connect.CodeOf(err))
40+
})
41+
}
42+
43+
func setupTokensService(t *testing.T) (*protocol.MockAPIInterface, v1connect.TokensServiceClient) {
44+
t.Helper()
45+
46+
ctrl := gomock.NewController(t)
47+
t.Cleanup(ctrl.Finish)
48+
49+
serverMock := protocol.NewMockAPIInterface(ctrl)
50+
51+
svc := NewTokensService(&FakeServerConnPool{api: serverMock}, experiments.NewAlwaysReturningDefaultValueClient())
52+
53+
_, handler := v1connect.NewTokensServiceHandler(svc, connect.WithInterceptors(auth.NewServerInterceptor()))
54+
55+
srv := httptest.NewServer(handler)
56+
t.Cleanup(srv.Close)
57+
58+
client := v1connect.NewTokensServiceClient(http.DefaultClient, srv.URL, connect.WithInterceptors(
59+
auth.NewClientInterceptor("auth-token"),
60+
))
61+
62+
return serverMock, client
63+
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/bufbuild/connect-go"
1414
"github.com/gitpod-io/gitpod/common-go/baseserver"
15+
"github.com/gitpod-io/gitpod/common-go/experiments"
1516
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
1617
"github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
1718
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
@@ -28,9 +29,9 @@ func TestPublicAPIServer_v1_WorkspaceService(t *testing.T) {
2829
gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1")
2930
require.NoError(t, err)
3031

31-
connPool := &proxy.NoConnectionPool{ServerAPI: gitpodAPI}
32+
connPool := proxy.ServerConnectionPool(&proxy.NoConnectionPool{ServerAPI: gitpodAPI})
3233

33-
require.NoError(t, register(srv, connPool))
34+
require.NoError(t, register(srv, connPool, experiments.NewAlwaysReturningDefaultValueClient()))
3435
baseserver.StartServerForTests(t, srv)
3536

3637
workspaceClient := v1connect.NewWorkspacesServiceClient(http.DefaultClient, srv.HTTPAddress(), connect.WithInterceptors(auth.NewClientInterceptor("some-token")))
@@ -57,9 +58,9 @@ func TestConnectWorkspaceService_RequiresAuth(t *testing.T) {
5758
gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1")
5859
require.NoError(t, err)
5960

60-
connPool := &proxy.NoConnectionPool{ServerAPI: gitpodAPI}
61+
connPool := proxy.ServerConnectionPool(&proxy.NoConnectionPool{ServerAPI: gitpodAPI})
6162

62-
require.NoError(t, register(srv, connPool))
63+
require.NoError(t, register(srv, connPool, experiments.NewAlwaysReturningDefaultValueClient()))
6364

6465
baseserver.StartServerForTests(t, srv)
6566

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313

1414
"github.com/bufbuild/connect-go"
15+
"github.com/gitpod-io/gitpod/common-go/experiments"
1516
"github.com/gitpod-io/gitpod/common-go/log"
1617

1718
"github.com/gitpod-io/gitpod/components/public-api/go/config"
@@ -40,6 +41,8 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
4041
return fmt.Errorf("failed to setup connection pool: %w", err)
4142
}
4243

44+
expClient := experiments.NewClient()
45+
4346
srv, err := baseserver.New("public_api_server",
4447
baseserver.WithLogger(logger),
4548
baseserver.WithConfig(cfg.Server),
@@ -70,7 +73,7 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
7073

7174
srv.HTTPMux().Handle("/stripe/invoices/webhook", handlers.ContentTypeHandler(stripeWebhookHandler, "application/json"))
7275

73-
if registerErr := register(srv, connPool); registerErr != nil {
76+
if registerErr := register(srv, connPool, expClient); registerErr != nil {
7477
return fmt.Errorf("failed to register services: %w", registerErr)
7578
}
7679

@@ -81,7 +84,7 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
8184
return nil
8285
}
8386

84-
func register(srv *baseserver.Server, connPool proxy.ServerConnectionPool) error {
87+
func register(srv *baseserver.Server, connPool proxy.ServerConnectionPool, expClient experiments.Client) error {
8588
proxy.RegisterMetrics(srv.MetricsRegistry())
8689

8790
connectMetrics := NewConnectMetrics()
@@ -104,7 +107,7 @@ func register(srv *baseserver.Server, connPool proxy.ServerConnectionPool) error
104107
teamsRoute, teamsServiceHandler := v1connect.NewTeamsServiceHandler(apiv1.NewTeamsService(connPool), handlerOptions...)
105108
srv.HTTPMux().Handle(teamsRoute, teamsServiceHandler)
106109

107-
tokensRoute, tokensServiceHandler := v1connect.NewTokensServiceHandler(apiv1.NewTokensService(), handlerOptions...)
110+
tokensRoute, tokensServiceHandler := v1connect.NewTokensServiceHandler(apiv1.NewTokensService(connPool, expClient), handlerOptions...)
108111
srv.HTTPMux().Handle(tokensRoute, tokensServiceHandler)
109112

110113
userRoute, userServiceHandler := v1connect.NewUserServiceHandler(apiv1.NewUserService(connPool), handlerOptions...)

0 commit comments

Comments
 (0)