This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 0bbbcb108 Refactor: simplify Github ApiClient creation logic - 4298 
(#4299)
0bbbcb108 is described below

commit 0bbbcb108e1b94b80583ae6897899dff0f37b0ab
Author: Klesh Wong <[email protected]>
AuthorDate: Thu Feb 2 19:06:05 2023 +0800

    Refactor: simplify Github ApiClient creation logic - 4298 (#4299)
    
    * fix: remove unused code
    
    * refactor: simplify github api client creation
    
    * fix: github unit test
---
 backend/helpers/pluginhelper/api/api_client.go     |  8 +++-
 .../api/apihelperabstract/api_client_abstract.go}  | 32 +++++----------
 .../api/apihelperabstract/connection_abstract.go   |  6 +++
 backend/plugins/ae/impl/impl.go                    |  2 +-
 backend/plugins/ae/models/connection.go            | 12 ------
 backend/plugins/github/api/blueprint_V200_test.go  | 18 +++++----
 backend/plugins/github/api/blueprint_test.go       | 18 +++++----
 backend/plugins/github/api/connection.go           | 11 +++---
 backend/plugins/github/models/connection.go        | 45 +++++++++++++++++++---
 backend/plugins/github/tasks/api_client.go         | 24 +++---------
 backend/plugins/gitlab/models/connection.go        |  1 -
 11 files changed, 97 insertions(+), 80 deletions(-)

diff --git a/backend/helpers/pluginhelper/api/api_client.go 
b/backend/helpers/pluginhelper/api/api_client.go
index 4b403f61f..c25230f04 100644
--- a/backend/helpers/pluginhelper/api/api_client.go
+++ b/backend/helpers/pluginhelper/api/api_client.go
@@ -63,7 +63,6 @@ type ApiClient struct {
 }
 
 // NewApiClientFromConnection creates ApiClient based on given connection.
-// The connection must
 func NewApiClientFromConnection(
        ctx gocontext.Context,
        br context.BasicRes,
@@ -73,6 +72,13 @@ func NewApiClientFromConnection(
        if err != nil {
                return nil, err
        }
+       // if connection needs to prepare the ApiClient, i.e. fetch token for 
future requests
+       if prepareApiClient, ok := 
connection.(apihelperabstract.PrepareApiClient); ok {
+               err = prepareApiClient.PrepareApiClient(apiClient)
+               if err != nil {
+                       return nil, err
+               }
+       }
        // if connection requires authorization
        if authenticator, ok := 
connection.(apihelperabstract.ApiAuthenticator); ok {
                apiClient.SetBeforeFunction(func(req *http.Request) 
errors.Error {
diff --git a/backend/plugins/github/models/connection.go 
b/backend/helpers/pluginhelper/api/apihelperabstract/api_client_abstract.go
similarity index 50%
copy from backend/plugins/github/models/connection.go
copy to 
backend/helpers/pluginhelper/api/apihelperabstract/api_client_abstract.go
index d5e853cd8..8399ff448 100644
--- a/backend/plugins/github/models/connection.go
+++ b/backend/helpers/pluginhelper/api/apihelperabstract/api_client_abstract.go
@@ -15,30 +15,18 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package models
+package apihelperabstract
 
 import (
-       helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-)
-
-type TestConnectionRequest struct {
-       Endpoint string `json:"endpoint" validate:"required,url"`
-       Token    string `json:"token" validate:"required"`
-       Proxy    string `json:"proxy"`
-}
+       "net/http"
+       "net/url"
 
-type GithubConnection struct {
-       helper.BaseConnection `mapstructure:",squash"`
-       helper.RestConnection `mapstructure:",squash"`
-       helper.AccessToken    `mapstructure:",squash"`
-       EnableGraphql         bool `mapstructure:"enableGraphql" 
json:"enableGraphql"`
-}
-
-func (GithubConnection) TableName() string {
-       return "_tool_github_connections"
-}
+       "github.com/apache/incubator-devlake/core/errors"
+)
 
-// Using GithubUserOfToken because it requires authentication, and it is 
public information anyway.
-type GithubUserOfToken struct {
-       Login string `json:"login"`
+// ApiClientAbstract defines the functionalities needed by all plugins for 
Synchronized API Request
+type ApiClientAbstract interface {
+       SetHeaders(headers map[string]string)
+       Get(path string, query url.Values, headers http.Header) 
(*http.Response, errors.Error)
+       Post(path string, query url.Values, body interface{}, headers 
http.Header) (*http.Response, errors.Error)
 }
diff --git 
a/backend/helpers/pluginhelper/api/apihelperabstract/connection_abstract.go 
b/backend/helpers/pluginhelper/api/apihelperabstract/connection_abstract.go
index 273cf90c6..39feaad0d 100644
--- a/backend/helpers/pluginhelper/api/apihelperabstract/connection_abstract.go
+++ b/backend/helpers/pluginhelper/api/apihelperabstract/connection_abstract.go
@@ -43,6 +43,12 @@ type ConnectionValidator interface {
        ValidateConnection(connection interface{}, valdator 
*validator.Validate) errors.Error
 }
 
+// PrepareApiClient is to be implemented by the concrete Connection which 
requires
+// preparation for the ApiClient created by NewApiClientFromConnection, i.e. 
fetch token for future requests
+type PrepareApiClient interface {
+       PrepareApiClient(apiClient ApiClientAbstract) errors.Error
+}
+
 // MultiAuth
 const (
        AUTH_METHOD_BASIC  = "BasicAuth"
diff --git a/backend/plugins/ae/impl/impl.go b/backend/plugins/ae/impl/impl.go
index 404ba70ec..998f00ace 100644
--- a/backend/plugins/ae/impl/impl.go
+++ b/backend/plugins/ae/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
        "fmt"
+
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
@@ -50,7 +51,6 @@ func (p AE) GetTablesInfo() []dal.Tabler {
                &models.AECommit{},
                &models.AEProject{},
                &models.AeConnection{},
-               &models.AeResponse{},
        }
 }
 
diff --git a/backend/plugins/ae/models/connection.go 
b/backend/plugins/ae/models/connection.go
index b6249e725..59d0a737c 100644
--- a/backend/plugins/ae/models/connection.go
+++ b/backend/plugins/ae/models/connection.go
@@ -30,7 +30,6 @@ import (
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-       
"github.com/apache/incubator-devlake/helpers/pluginhelper/api/apihelperabstract"
 )
 
 type AeAppKey helper.AppKey
@@ -47,10 +46,6 @@ func (aak AeAppKey) SetupAuthentication(req *http.Request) 
errors.Error {
        return nil
 }
 
-func (aak AeAppKey) GetAppKeyAuthenticator() 
apihelperabstract.ApiAuthenticator {
-       return aak
-}
-
 // AeConn holds the essential information to connect to the AE API
 type AeConn struct {
        helper.RestConnection `mapstructure:",squash"`
@@ -64,13 +59,6 @@ type AeConnection struct {
        AeAppKey              `mapstructure:",squash"`
 }
 
-// This object conforms to what the frontend currently expects.
-type AeResponse struct {
-       AeConnection
-       Name string `json:"name"`
-       ID   int    `json:"id"`
-}
-
 func (AeConnection) TableName() string {
        return "_tool_ae_connections"
 }
diff --git a/backend/plugins/github/api/blueprint_V200_test.go 
b/backend/plugins/github/api/blueprint_V200_test.go
index f17b6edc2..5df199f2d 100644
--- a/backend/plugins/github/api/blueprint_V200_test.go
+++ b/backend/plugins/github/api/blueprint_V200_test.go
@@ -42,13 +42,17 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
                                ID: 1,
                        },
                },
-               RestConnection: helper.RestConnection{
-                       Endpoint:         "https://api.github.com/";,
-                       Proxy:            "",
-                       RateLimitPerHour: 0,
-               },
-               AccessToken: helper.AccessToken{
-                       Token: "123",
+               GithubConn: models.GithubConn{
+                       RestConnection: helper.RestConnection{
+                               Endpoint:         "https://api.github.com/";,
+                               Proxy:            "",
+                               RateLimitPerHour: 0,
+                       },
+                       GithubAccessToken: models.GithubAccessToken{
+                               AccessToken: helper.AccessToken{
+                                       Token: "123",
+                               },
+                       },
                },
        }
        mockMeta := mockplugin.NewPluginMeta(t)
diff --git a/backend/plugins/github/api/blueprint_test.go 
b/backend/plugins/github/api/blueprint_test.go
index 0add557eb..f6193e05d 100644
--- a/backend/plugins/github/api/blueprint_test.go
+++ b/backend/plugins/github/api/blueprint_test.go
@@ -68,13 +68,17 @@ func TestMakePipelinePlan(t *testing.T) {
                                ID: 1,
                        },
                },
-               RestConnection: helper.RestConnection{
-                       Endpoint:         "https://api.github.com/";,
-                       Proxy:            "",
-                       RateLimitPerHour: 0,
-               },
-               AccessToken: helper.AccessToken{
-                       Token: "123",
+               GithubConn: models.GithubConn{
+                       RestConnection: helper.RestConnection{
+                               Endpoint:         "https://api.github.com/";,
+                               Proxy:            "",
+                               RateLimitPerHour: 0,
+                       },
+                       GithubAccessToken: models.GithubAccessToken{
+                               AccessToken: helper.AccessToken{
+                                       Token: "123",
+                               },
+                       },
                },
        }
        scopes := make([]*plugin.BlueprintScopeV100, 0)
diff --git a/backend/plugins/github/api/connection.go 
b/backend/plugins/github/api/connection.go
index 70aae85cc..fbb90ab4b 100644
--- a/backend/plugins/github/api/connection.go
+++ b/backend/plugins/github/api/connection.go
@@ -20,14 +20,15 @@ package api
 import (
        "context"
        "fmt"
+       "net/http"
+       "strings"
+       "time"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
        "github.com/apache/incubator-devlake/server/api/shared"
-       "net/http"
-       "strings"
-       "time"
 )
 
 type GithubTestConnResponse struct {
@@ -38,14 +39,14 @@ type GithubTestConnResponse struct {
 // @Summary test github connection
 // @Description Test github Connection
 // @Tags plugins/github
-// @Param body body models.TestConnectionRequest true "json body"
+// @Param body body models.GithubConn true "json body"
 // @Success 200  {object} GithubTestConnResponse
 // @Failure 400  {string} errcode.Error "Bad Request"
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/github/test [POST]
 func TestConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        // process input
-       var params models.TestConnectionRequest
+       var params models.GithubConn
        err := api.Decode(input.Body, &params, vld)
        if err != nil {
                return nil, err
diff --git a/backend/plugins/github/models/connection.go 
b/backend/plugins/github/models/connection.go
index d5e853cd8..d634c4889 100644
--- a/backend/plugins/github/models/connection.go
+++ b/backend/plugins/github/models/connection.go
@@ -18,19 +18,52 @@ limitations under the License.
 package models
 
 import (
+       "fmt"
+       "net/http"
+       "strings"
+
+       "github.com/apache/incubator-devlake/core/errors"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       
"github.com/apache/incubator-devlake/helpers/pluginhelper/api/apihelperabstract"
 )
 
-type TestConnectionRequest struct {
-       Endpoint string `json:"endpoint" validate:"required,url"`
-       Token    string `json:"token" validate:"required"`
-       Proxy    string `json:"proxy"`
+// GithubAccessToken supports fetching data with multiple tokens
+type GithubAccessToken struct {
+       helper.AccessToken `mapstructure:",squash"`
+       tokens             []string `gorm:"-" json:"-" mapstructure:"-"`
+       tokenIndex         int      `gorm:"-" json:"-" mapstructure:"-"`
+}
+
+// GithubConn holds the essential information to connect to the Github API
+type GithubConn struct {
+       helper.RestConnection `mapstructure:",squash"`
+       GithubAccessToken     `mapstructure:",squash"`
+}
+
+// PrepareApiClient splits Token to tokens for SetupAuthentication to utilize
+func (conn *GithubConn) PrepareApiClient(apiClient 
apihelperabstract.ApiClientAbstract) errors.Error {
+       conn.tokens = strings.Split(conn.Token, ",")
+       return nil
 }
 
+// SetupAuthentication sets up the HTTP Request Authentication
+func (gat *GithubAccessToken) SetupAuthentication(req *http.Request) 
errors.Error {
+       // Rotates token on each request.
+       req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", 
gat.tokens[gat.tokenIndex]))
+       // Set next token index
+       gat.tokenIndex = (gat.tokenIndex + 1) % len(gat.tokens)
+       return nil
+}
+
+// GetTokensCount returns total number of tokens
+func (gat *GithubAccessToken) GetTokensCount() int {
+       return len(gat.tokens)
+}
+
+// GithubConnection holds GithubConn plus ID/Name for database storage
 type GithubConnection struct {
        helper.BaseConnection `mapstructure:",squash"`
-       helper.RestConnection `mapstructure:",squash"`
-       helper.AccessToken    `mapstructure:",squash"`
+       GithubConn            `mapstructure:",squash"`
        EnableGraphql         bool `mapstructure:"enableGraphql" 
json:"enableGraphql"`
 }
 
diff --git a/backend/plugins/github/tasks/api_client.go 
b/backend/plugins/github/tasks/api_client.go
index 6d3bb0c39..2299f649f 100644
--- a/backend/plugins/github/tasks/api_client.go
+++ b/backend/plugins/github/tasks/api_client.go
@@ -18,33 +18,21 @@ limitations under the License.
 package tasks
 
 import (
-       "fmt"
+       "net/http"
+       "strconv"
+       "time"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "net/http"
-       "strconv"
-       "strings"
-       "time"
 )
 
 func CreateApiClient(taskCtx plugin.TaskContext, connection 
*models.GithubConnection) (*api.ApiAsyncClient, errors.Error) {
-       // load configuration
-       tokens := strings.Split(connection.Token, ",")
-       tokenIndex := 0
-       // create synchronize api client so we can calculate api rate limit 
dynamically
-       apiClient, err := api.NewApiClient(taskCtx.GetContext(), 
connection.Endpoint, nil, 0, connection.Proxy, taskCtx)
+       apiClient, err := api.NewApiClientFromConnection(taskCtx.GetContext(), 
taskCtx, connection)
        if err != nil {
                return nil, err
        }
-       // Rotates token on each request.
-       apiClient.SetBeforeFunction(func(req *http.Request) errors.Error {
-               req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", 
tokens[tokenIndex]))
-               // Set next token index
-               tokenIndex = (tokenIndex + 1) % len(tokens)
-               return nil
-       })
 
        // create rate limit calculator
        rateLimiter := &api.ApiRateLimitCalculator{
@@ -74,7 +62,7 @@ func CreateApiClient(taskCtx plugin.TaskContext, connection 
*models.GithubConnec
                        // so, we calculate the rate limit of a single token, 
and presume all tokens are the same, to
                        // simplify the algorithm for now
                        // TODO: consider different token has different 
rate-limit
-                       return rateLimit * len(tokens), 1 * time.Hour, nil
+                       return rateLimit * connection.GetTokensCount(), 1 * 
time.Hour, nil
                },
        }
        asyncApiClient, err := api.CreateAsyncApiClient(
diff --git a/backend/plugins/gitlab/models/connection.go 
b/backend/plugins/gitlab/models/connection.go
index 0b7b299b7..31cee6b05 100644
--- a/backend/plugins/gitlab/models/connection.go
+++ b/backend/plugins/gitlab/models/connection.go
@@ -27,7 +27,6 @@ type GitlabConn struct {
        helper.AccessToken    `mapstructure:",squash"`
 }
 
-// This object conforms to what the frontend currently sends.
 // GitlabConnection holds GitlabConn plus ID/Name for database storage
 type GitlabConnection struct {
        helper.BaseConnection `mapstructure:",squash"`

Reply via email to