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 25c68b456 refactor: simplify feishui apiclient creation (#4312)
25c68b456 is described below

commit 25c68b456d0f89038b9ab70cb336156e70fff982
Author: Klesh Wong <[email protected]>
AuthorDate: Fri Feb 3 16:23:56 2023 +0800

    refactor: simplify feishui apiclient creation (#4312)
    
    * refactor: simplify feishui apiclient creation
    
    * fix: feishui linting
    
    * fix: feishui linting
---
 .../helpers/pluginhelper/api/connection_auths.go   | 27 +++++++-------
 backend/plugins/ae/api/connection.go               |  2 +-
 backend/plugins/ae/models/connection.go            |  5 ++-
 backend/plugins/azure/api/connection.go            |  2 +-
 backend/plugins/bitbucket/api/connection.go        |  2 +-
 backend/plugins/feishu/api/connection.go           | 37 +++++--------------
 backend/plugins/feishu/models/connection.go        | 42 ++++++++++++++++++----
 backend/plugins/feishu/tasks/api_client.go         | 36 ++-----------------
 .../tasks/meeting_top_user_item_collector.go       |  9 ++---
 9 files changed, 71 insertions(+), 91 deletions(-)

diff --git a/backend/helpers/pluginhelper/api/connection_auths.go 
b/backend/helpers/pluginhelper/api/connection_auths.go
index 6e43c31c2..50c0973bb 100644
--- a/backend/helpers/pluginhelper/api/connection_auths.go
+++ b/backend/helpers/pluginhelper/api/connection_auths.go
@@ -35,12 +35,12 @@ type BasicAuth struct {
 }
 
 // GetEncodedToken returns encoded bearer token for HTTP Basic Authentication
-func (ba BasicAuth) GetEncodedToken() string {
+func (ba *BasicAuth) GetEncodedToken() string {
        return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", 
ba.Username, ba.Password)))
 }
 
 // SetupAuthentication sets up the request headers for authentication
-func (ba BasicAuth) SetupAuthentication(request *http.Request) errors.Error {
+func (ba *BasicAuth) SetupAuthentication(request *http.Request) errors.Error {
        request.Header.Set("Authorization", fmt.Sprintf("Basic %v", 
ba.GetEncodedToken()))
        return nil
 }
@@ -49,7 +49,7 @@ func (ba BasicAuth) SetupAuthentication(request 
*http.Request) errors.Error {
 // it looks odd to return itself with a different type, this is necessary 
because Callers
 // might call the method from the 
Outer-Struct(`connection.SetupAuthentication(...)`)
 // which would lead to a Stack Overflow  error
-func (ba BasicAuth) GetBasicAuthenticator() apihelperabstract.ApiAuthenticator 
{
+func (ba *BasicAuth) GetBasicAuthenticator() 
apihelperabstract.ApiAuthenticator {
        return ba
 }
 
@@ -59,13 +59,13 @@ type AccessToken struct {
 }
 
 // SetupAuthentication sets up the request headers for authentication
-func (at AccessToken) SetupAuthentication(request *http.Request) errors.Error {
+func (at *AccessToken) SetupAuthentication(request *http.Request) errors.Error 
{
        request.Header.Set("Authorization", fmt.Sprintf("Bearer %v", at.Token))
        return nil
 }
 
 // GetAccessTokenAuthenticator returns SetupAuthentication
-func (at AccessToken) GetAccessTokenAuthenticator() 
apihelperabstract.ApiAuthenticator {
+func (at *AccessToken) GetAccessTokenAuthenticator() 
apihelperabstract.ApiAuthenticator {
        return at
 }
 
@@ -76,17 +76,18 @@ type AppKey struct {
 }
 
 // SetupAuthentication sets up the request headers for authentication
-func (ak AppKey) SetupAuthentication(request *http.Request) errors.Error {
+func (ak *AppKey) SetupAuthentication(request *http.Request) errors.Error {
        // no universal way to implement AppKey authentication, plugin should 
alias AppKey and
-       // define its own implementation
-       panic("not implemented")
+       // define its own implementation if API requires signature for each 
request,
+       // or you should implement PrepareApiClient if API requires a Token for 
all requests
+       return nil
 }
 
 // GetAppKeyAuthenticator returns SetupAuthentication
-func (ak AppKey) GetAppKeyAuthenticator() apihelperabstract.ApiAuthenticator {
+func (ak *AppKey) GetAppKeyAuthenticator() apihelperabstract.ApiAuthenticator {
        // no universal way to implement AppKey authentication, plugin should 
alias AppKey and
        // define its own implementation
-       panic("not implemented")
+       return ak
 }
 
 // MultiAuth implements the MultiAuthenticator interface
@@ -95,7 +96,7 @@ type MultiAuth struct {
        apiAuthenticator apihelperabstract.ApiAuthenticator
 }
 
-func (ma MultiAuth) GetApiAuthenticator(connection 
apihelperabstract.ApiConnection) (apihelperabstract.ApiAuthenticator, 
errors.Error) {
+func (ma *MultiAuth) GetApiAuthenticator(connection 
apihelperabstract.ApiConnection) (apihelperabstract.ApiAuthenticator, 
errors.Error) {
        // cache the ApiAuthenticator for performance
        if ma.apiAuthenticator != nil {
                return ma.apiAuthenticator, nil
@@ -134,7 +135,7 @@ func (ma MultiAuth) GetApiAuthenticator(connection 
apihelperabstract.ApiConnecti
 // Specific Connection should implement IAuthentication and then call this 
method for MultiAuth to work properly,
 // check jira/models/connection.go:JiraConn if you needed an example
 // Note: this method would be called for each request, so it is 
performance-sensitive, do NOT use reflection here
-func (ma MultiAuth) SetupAuthenticationForConnection(connection 
apihelperabstract.ApiConnection, req *http.Request) errors.Error {
+func (ma *MultiAuth) SetupAuthenticationForConnection(connection 
apihelperabstract.ApiConnection, req *http.Request) errors.Error {
        apiAuthenticator, err := ma.GetApiAuthenticator(connection)
        if err != nil {
                return err
@@ -142,7 +143,7 @@ func (ma MultiAuth) 
SetupAuthenticationForConnection(connection apihelperabstrac
        return apiAuthenticator.SetupAuthentication(req)
 }
 
-func (ma MultiAuth) ValidateConnection(connection interface{}, v 
*validator.Validate) errors.Error {
+func (ma *MultiAuth) ValidateConnection(connection interface{}, v 
*validator.Validate) errors.Error {
        // the idea is to filtered out errors from unselected Authentication 
struct
        validationErrors := v.Struct(connection).(validator.ValidationErrors)
        if validationErrors != nil {
diff --git a/backend/plugins/ae/api/connection.go 
b/backend/plugins/ae/api/connection.go
index 31e1e5af1..ae1ba0a28 100644
--- a/backend/plugins/ae/api/connection.go
+++ b/backend/plugins/ae/api/connection.go
@@ -48,7 +48,7 @@ func TestConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput,
                return nil, errors.BadInput.Wrap(err, "could not decode request 
parameters")
        }
 
-       apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, connection)
+       apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, &connection)
        if err != nil {
                return nil, err
        }
diff --git a/backend/plugins/ae/models/connection.go 
b/backend/plugins/ae/models/connection.go
index 59d0a737c..40bbcf8be 100644
--- a/backend/plugins/ae/models/connection.go
+++ b/backend/plugins/ae/models/connection.go
@@ -35,7 +35,7 @@ import (
 type AeAppKey helper.AppKey
 
 // SetupAuthentication sets up the HTTP Request Authentication
-func (aak AeAppKey) SetupAuthentication(req *http.Request) errors.Error {
+func (aak *AeAppKey) SetupAuthentication(req *http.Request) errors.Error {
        nonceStr := plugin.RandLetterBytes(8)
        timestamp := fmt.Sprintf("%v", time.Now().Unix())
        sign := signRequest(req.URL.Query(), aak.AppId, aak.SecretKey, 
nonceStr, timestamp)
@@ -55,8 +55,7 @@ type AeConn struct {
 // AeConnection holds AeConn plus ID/Name for database storage
 type AeConnection struct {
        helper.BaseConnection `mapstructure:",squash"`
-       helper.RestConnection `mapstructure:",squash"`
-       AeAppKey              `mapstructure:",squash"`
+       AeConn                `mapstructure:",squash"`
 }
 
 func (AeConnection) TableName() string {
diff --git a/backend/plugins/azure/api/connection.go 
b/backend/plugins/azure/api/connection.go
index 5648c4825..8cba156e8 100644
--- a/backend/plugins/azure/api/connection.go
+++ b/backend/plugins/azure/api/connection.go
@@ -42,7 +42,7 @@ func TestConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput,
                return nil, errors.BadInput.Wrap(err, "could not decode request 
parameters")
        }
        // test connection
-       apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, connection)
+       apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, &connection)
        if err != nil {
                return nil, err
        }
diff --git a/backend/plugins/bitbucket/api/connection.go 
b/backend/plugins/bitbucket/api/connection.go
index 0479d2f89..681c18aa0 100644
--- a/backend/plugins/bitbucket/api/connection.go
+++ b/backend/plugins/bitbucket/api/connection.go
@@ -44,7 +44,7 @@ func TestConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput,
                return nil, errors.BadInput.Wrap(err, "could not decode request 
parameters")
        }
        // test connection
-       apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, connection)
+       apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, &connection)
        if err != nil {
                return nil, err
        }
diff --git a/backend/plugins/feishu/api/connection.go 
b/backend/plugins/feishu/api/connection.go
index 537714d2b..c9075bc2d 100644
--- a/backend/plugins/feishu/api/connection.go
+++ b/backend/plugins/feishu/api/connection.go
@@ -19,53 +19,34 @@ package api
 
 import (
        "context"
+       "net/http"
+
        "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/feishu/apimodels"
        "github.com/apache/incubator-devlake/plugins/feishu/models"
-       "net/http"
 )
 
 // @Summary test feishu connection
-// @Description Test feishu Connection
+// @Description Test feishu Connection. endpoint: 
https://open.feishu.cn/open-apis/
 // @Tags plugins/feishu
-// @Param body body models.TestConnectionRequest true "json body"
+// @Param body body models.FeishuConn true "json body"
 // @Success 200  {object} shared.ApiBody "Success"
 // @Failure 400  {string} errcode.Error "Bad Request"
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/feishu/test [POST]
 func TestConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        // process input
-       var params models.TestConnectionRequest
-       if err := api.Decode(input.Body, &params, vld); err != nil {
+       var connection models.FeishuConn
+       if err := api.Decode(input.Body, &connection, vld); err != nil {
                return nil, errors.BadInput.Wrap(err, "could not decode request 
parameters")
        }
-       authApiClient, err := api.NewApiClient(context.TODO(), params.Endpoint, 
nil, 0, params.Proxy, basicRes)
-       if err != nil {
-               return nil, err
-       }
 
-       // request for access token
-       tokenReqBody := &apimodels.ApiAccessTokenRequest{
-               AppId:     params.AppId,
-               AppSecret: params.SecretKey,
-       }
-       tokenRes, err := 
authApiClient.Post("open-apis/auth/v3/tenant_access_token/internal", nil, 
tokenReqBody, nil)
-       if err != nil {
-               return nil, err
-       }
-       tokenResBody := &apimodels.ApiAccessTokenResponse{}
-       err = api.UnmarshalResponse(tokenRes, tokenResBody)
-       if err != nil {
-               return nil, err
-       }
-       if tokenResBody.AppAccessToken == "" && tokenResBody.TenantAccessToken 
== "" {
-               return nil, errors.Default.New("failed to request access token")
-       }
+       // test connection
+       _, err := api.NewApiClientFromConnection(context.TODO(), basicRes, 
&connection)
 
        // output
-       return nil, nil
+       return nil, err
 }
 
 // @Summary create feishu connection
diff --git a/backend/plugins/feishu/models/connection.go 
b/backend/plugins/feishu/models/connection.go
index 6c5f577c3..3069968d5 100644
--- a/backend/plugins/feishu/models/connection.go
+++ b/backend/plugins/feishu/models/connection.go
@@ -18,20 +18,48 @@ limitations under the License.
 package models
 
 import (
+       "fmt"
+
+       "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"
+       "github.com/apache/incubator-devlake/plugins/feishu/apimodels"
 )
 
-type TestConnectionRequest struct {
-       Endpoint  string `json:"endpoint" validate:"required,url"`
-       AppId     string `mapstructure:"app_id" validate:"required" 
json:"app_id"`
-       SecretKey string `mapstructure:"secret_key" validate:"required" 
json:"secret_key"`
-       Proxy     string `json:"proxy"`
+// FeishuConn holds the essential information to connect to the Feishu API
+type FeishuConn struct {
+       helper.RestConnection `mapstructure:",squash"`
+       helper.AppKey         `mapstructure:",squash"`
 }
 
+func (conn *FeishuConn) PrepareApiClient(apiClient 
apihelperabstract.ApiClientAbstract) errors.Error {
+       // request for access token
+       tokenReqBody := &apimodels.ApiAccessTokenRequest{
+               AppId:     conn.AppId,
+               AppSecret: conn.SecretKey,
+       }
+       tokenRes, err := apiClient.Post("auth/v3/tenant_access_token/internal", 
nil, tokenReqBody, nil)
+       if err != nil {
+               return err
+       }
+       tokenResBody := &apimodels.ApiAccessTokenResponse{}
+       err = helper.UnmarshalResponse(tokenRes, tokenResBody)
+       if err != nil {
+               return err
+       }
+       if tokenResBody.AppAccessToken == "" && tokenResBody.TenantAccessToken 
== "" {
+               return errors.Default.New("failed to request access token")
+       }
+       apiClient.SetHeaders(map[string]string{
+               "Authorization": fmt.Sprintf("Bearer %v", 
tokenResBody.TenantAccessToken),
+       })
+       return nil
+}
+
+// FeishuConnection holds FeishuConn plus ID/Name for database storage
 type FeishuConnection struct {
        helper.BaseConnection `mapstructure:",squash"`
-       helper.RestConnection `mapstructure:",squash"`
-       helper.AppKey         `mapstructure:",squash"`
+       FeishuConn            `mapstructure:",squash"`
 }
 
 func (FeishuConnection) TableName() string {
diff --git a/backend/plugins/feishu/tasks/api_client.go 
b/backend/plugins/feishu/tasks/api_client.go
index 7b656fb8f..e015056d3 100644
--- a/backend/plugins/feishu/tasks/api_client.go
+++ b/backend/plugins/feishu/tasks/api_client.go
@@ -18,50 +18,20 @@ limitations under the License.
 package tasks
 
 import (
-       "fmt"
        "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/feishu/apimodels"
        "github.com/apache/incubator-devlake/plugins/feishu/models"
 )
 
-const AUTH_ENDPOINT = "https://open.feishu.cn";
-const ENDPOINT = "https://open.feishu.cn/open-apis/vc/v1";
+// const AUTH_ENDPOINT = "https://open.feishu.cn";
+// const ENDPOINT = "https://open.feishu.cn/open-apis/vc/v1";
 
 func NewFeishuApiClient(taskCtx plugin.TaskContext, connection 
*models.FeishuConnection) (*api.ApiAsyncClient, errors.Error) {
-
-       authApiClient, err := api.NewApiClient(taskCtx.GetContext(), 
AUTH_ENDPOINT, nil, 0, connection.Proxy, taskCtx)
-       if err != nil {
-               return nil, err
-       }
-
-       // request for access token
-       tokenReqBody := &apimodels.ApiAccessTokenRequest{
-               AppId:     connection.AppId,
-               AppSecret: connection.SecretKey,
-       }
-       tokenRes, err := 
authApiClient.Post("open-apis/auth/v3/tenant_access_token/internal", nil, 
tokenReqBody, nil)
-       if err != nil {
-               return nil, err
-       }
-       tokenResBody := &apimodels.ApiAccessTokenResponse{}
-       err = api.UnmarshalResponse(tokenRes, tokenResBody)
+       apiClient, err := api.NewApiClientFromConnection(taskCtx.GetContext(), 
taskCtx, connection)
        if err != nil {
                return nil, err
        }
-       if tokenResBody.AppAccessToken == "" && tokenResBody.TenantAccessToken 
== "" {
-               return nil, errors.Default.New("failed to request access token")
-       }
-       // real request apiClient
-       apiClient, err := api.NewApiClient(taskCtx.GetContext(), ENDPOINT, nil, 
0, connection.Proxy, taskCtx)
-       if err != nil {
-               return nil, err
-       }
-       // set token
-       apiClient.SetHeaders(map[string]string{
-               "Authorization": fmt.Sprintf("Bearer %v", 
tokenResBody.TenantAccessToken),
-       })
 
        // create async api client
        asyncApiCLient, err := api.CreateAsyncApiClient(taskCtx, apiClient, 
&api.ApiRateLimitCalculator{
diff --git a/backend/plugins/feishu/tasks/meeting_top_user_item_collector.go 
b/backend/plugins/feishu/tasks/meeting_top_user_item_collector.go
index 05c7174eb..a803c023c 100644
--- a/backend/plugins/feishu/tasks/meeting_top_user_item_collector.go
+++ b/backend/plugins/feishu/tasks/meeting_top_user_item_collector.go
@@ -19,13 +19,14 @@ package tasks
 
 import (
        "encoding/json"
+       "net/http"
+       "net/url"
+       "strconv"
+
        "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/feishu/apimodels"
-       "net/http"
-       "net/url"
-       "strconv"
 )
 
 const RAW_MEETING_TOP_USER_ITEM_TABLE = "feishu_meeting_top_user_item"
@@ -51,7 +52,7 @@ func CollectMeetingTopUserItem(taskCtx plugin.SubTaskContext) 
errors.Error {
                ApiClient:   data.ApiClient,
                Incremental: false,
                Input:       iterator,
-               UrlTemplate: "/reports/get_top_user",
+               UrlTemplate: "vc/v1/reports/get_top_user",
                Query: func(reqData *api.RequestData) (url.Values, 
errors.Error) {
                        query := url.Values{}
                        input := reqData.Input.(*api.DatePair)

Reply via email to