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, ¶ms, 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)