Skip to content

Commit 6510b00

Browse files
committed
refactor(github): extract shared authenticated http client from api client
- moved token provider and refresh round tripper setup into a reusable helper - introduced CreateAuthenticatedHttpClient to centralize auth + transport logic - updated CreateApiClient to use shared http client instead of inline setup Rationale: - decouples authentication (transport layer) from REST-specific client logic - enables reuse for GraphQL client without duplicating token refresh logic - aligns architecture with separation of concerns (http transport vs api clients)
1 parent 39d017d commit 6510b00

2 files changed

Lines changed: 82 additions & 35 deletions

File tree

backend/plugins/github/tasks/api_client.go

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"github.com/apache/incubator-devlake/core/plugin"
2727
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
2828
"github.com/apache/incubator-devlake/plugins/github/models"
29-
"github.com/apache/incubator-devlake/plugins/github/token"
3029
)
3130

3231
func CreateApiClient(taskCtx plugin.TaskContext, connection *models.GithubConnection) (*api.ApiAsyncClient, errors.Error) {
@@ -35,40 +34,10 @@ func CreateApiClient(taskCtx plugin.TaskContext, connection *models.GithubConnec
3534
return nil, err
3635
}
3736

38-
logger := taskCtx.GetLogger()
39-
db := taskCtx.GetDal()
40-
encryptionSecret := taskCtx.GetConfig(plugin.EncodeKeyEnvStr)
41-
42-
// Inject TokenProvider for OAuth refresh or GitHub App installation tokens.
43-
var tp *token.TokenProvider
44-
if connection.RefreshToken != "" {
45-
tp = token.NewTokenProvider(connection, db, apiClient.GetClient(), logger, encryptionSecret)
46-
} else if connection.AuthMethod == models.AppKey && connection.InstallationID != 0 {
47-
tp = token.NewAppInstallationTokenProvider(connection, db, apiClient.GetClient(), logger, encryptionSecret)
48-
}
49-
if tp != nil {
50-
// Wrap the transport
51-
baseTransport := apiClient.GetClient().Transport
52-
if baseTransport == nil {
53-
baseTransport = http.DefaultTransport
54-
}
55-
56-
rt := token.NewRefreshRoundTripper(baseTransport, tp)
57-
apiClient.GetClient().Transport = rt
58-
logger.Info("Installed token refresh round tripper for connection %d (authMethod=%s)",
59-
connection.ID, connection.AuthMethod)
60-
}
61-
62-
// Persist the freshly minted token so the DB has a correctly encrypted value.
63-
// PrepareApiClient (called by NewApiClientFromConnection) mints the token
64-
// in-memory but does not persist it; without this, the DB may contain a stale
65-
// or corrupted token that breaks GET /connections.
66-
if connection.AuthMethod == models.AppKey && connection.Token != "" {
67-
if err := token.PersistEncryptedTokenColumns(db, connection, encryptionSecret, logger, false); err != nil {
68-
logger.Warn(err, "Failed to persist initial token for connection %d", connection.ID)
69-
} else {
70-
logger.Info("Persisted initial token for connection %d", connection.ID)
71-
}
37+
// inject the shared auth layer
38+
_, err = CreateAuthenticatedHttpClient(taskCtx, connection, apiClient.GetClient())
39+
if err != nil {
40+
return nil, err
7241
}
7342

7443
// create rate limit calculator
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package tasks
19+
20+
import (
21+
"net/http"
22+
23+
"github.com/apache/incubator-devlake/core/errors"
24+
"github.com/apache/incubator-devlake/core/plugin"
25+
"github.com/apache/incubator-devlake/plugins/github/models"
26+
"github.com/apache/incubator-devlake/plugins/github/token"
27+
)
28+
29+
func CreateAuthenticatedHttpClient(
30+
taskCtx plugin.TaskContext,
31+
connection *models.GithubConnection,
32+
baseClient *http.Client,
33+
) (*http.Client, errors.Error) {
34+
35+
logger := taskCtx.GetLogger()
36+
db := taskCtx.GetDal()
37+
encryptionSecret := taskCtx.GetConfig(plugin.EncodeKeyEnvStr)
38+
39+
if baseClient == nil {
40+
baseClient = &http.Client{}
41+
}
42+
43+
// Inject TokenProvider for OAuth refresh or GitHub App installation tokens.
44+
var tp *token.TokenProvider
45+
if connection.RefreshToken != "" {
46+
tp = token.NewTokenProvider(connection, db, baseClient, logger, encryptionSecret)
47+
} else if connection.AuthMethod == models.AppKey && connection.InstallationID != 0 {
48+
tp = token.NewAppInstallationTokenProvider(connection, db, baseClient, logger, encryptionSecret)
49+
}
50+
51+
if tp != nil {
52+
baseTransport := baseClient.Transport
53+
if baseTransport == nil {
54+
baseTransport = http.DefaultTransport
55+
}
56+
57+
baseClient.Transport = token.NewRefreshRoundTripper(baseTransport, tp)
58+
logger.Info(
59+
"Installed token refresh round tripper for connection %d (authMethod=%s)",
60+
connection.ID,
61+
connection.AuthMethod,
62+
)
63+
}
64+
65+
// Persist the freshly minted token so the DB has a correctly encrypted value.
66+
// PrepareApiClient (called by NewApiClientFromConnection) mints the token
67+
// in-memory but does not persist it; without this, the DB may contain a stale
68+
// or corrupted token that breaks GET /connections.
69+
if connection.AuthMethod == models.AppKey && connection.Token != "" {
70+
if err := token.PersistEncryptedTokenColumns(db, connection, encryptionSecret, logger, false); err != nil {
71+
logger.Warn(err, "Failed to persist initial token for connection %d", connection.ID)
72+
} else {
73+
logger.Info("Persisted initial token for connection %d", connection.ID)
74+
}
75+
}
76+
77+
return baseClient, nil
78+
}

0 commit comments

Comments
 (0)