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 9c00c4de Feishu plugin support multi-connections. (#2322)
9c00c4de is described below
commit 9c00c4de92fff2e96a6e744792c860a7d7d86a2f
Author: likyh <[email protected]>
AuthorDate: Mon Jun 27 14:20:38 2022 +0800
Feishu plugin support multi-connections. (#2322)
* Feishu plugin support multi-connections.
* fix bug
* fix a bug and ci
Co-authored-by: linyh <[email protected]>
---
plugins/feishu/api/connection.go | 133 +++++++++++++++++++++
plugins/feishu/{tasks/task_data.go => api/init.go} | 29 ++---
.../register.go => apimodels/access_token.go} | 20 ++--
plugins/feishu/e2e/meeting_test.go | 61 ++++++++++
.../_raw_feishu_meeting_top_user_item.csv | 29 +++++
.../_tool_feishu_meeting_top_user_items.csv | 29 +++++
plugins/feishu/feishu.go | 74 ++----------
plugins/feishu/{feishu.go => impl/impl.go} | 72 +++++++----
.../{meeting_top_user_item.go => connection.go} | 26 ++--
plugins/feishu/models/meeting_top_user_item.go | 1 +
.../{init_schema.go => archived/connection.go} | 29 ++---
.../archived/meeting_top_user_item.go | 17 ++-
.../feishu/models/migrationscripts/init_schema.go | 45 ++++++-
plugins/feishu/models/migrationscripts/register.go | 2 +-
.../migrationscripts/updateSchemas20220526.go | 90 --------------
plugins/feishu/tasks/api_client.go | 47 ++------
.../tasks/meeting_top_user_item_collector.go | 5 +-
.../tasks/meeting_top_user_item_extractor.go | 6 +-
plugins/feishu/tasks/task_data.go | 6 +-
.../e2e/snapshot_tables/issue_commits_story.csv | 2 +
20 files changed, 435 insertions(+), 288 deletions(-)
diff --git a/plugins/feishu/api/connection.go b/plugins/feishu/api/connection.go
new file mode 100644
index 00000000..a88e84fb
--- /dev/null
+++ b/plugins/feishu/api/connection.go
@@ -0,0 +1,133 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/plugins/feishu/apimodels"
+ "github.com/apache/incubator-devlake/plugins/feishu/models"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/mitchellh/mapstructure"
+ "net/http"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+)
+
+/*
+POST /plugins/feishu/test
+*/
+func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput,
error) {
+ // process input
+ var params models.TestConnectionRequest
+ err := mapstructure.Decode(input.Body, ¶ms)
+ if err != nil {
+ return nil, err
+ }
+ err = vld.Struct(params)
+ if err != nil {
+ return nil, err
+ }
+
+ authApiClient, err := helper.NewApiClient(params.Endpoint, nil, 0,
params.Proxy, nil)
+ 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 = helper.UnmarshalResponse(tokenRes, tokenResBody)
+ if err != nil {
+ return nil, err
+ }
+ if tokenResBody.AppAccessToken == "" && tokenResBody.TenantAccessToken
== "" {
+ return nil, fmt.Errorf("failed to request access token")
+ }
+
+ // output
+ return nil, nil
+}
+
+/*
+POST /plugins/feishu/connections
+*/
+func PostConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput,
error) {
+ connection := &models.FeishuConnection{}
+ err := connectionHelper.Create(connection, input)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connection, Status:
http.StatusOK}, nil
+}
+
+/*
+PATCH /plugins/feishu/connections/:connectionId
+*/
+func PatchConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput,
error) {
+ connection := &models.FeishuConnection{}
+ err := connectionHelper.Patch(connection, input)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connection, Status:
http.StatusOK}, nil
+}
+
+/*
+DELETE /plugins/feishu/connections/:connectionId
+*/
+func DeleteConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput,
error) {
+ connection := &models.FeishuConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ if err != nil {
+ return nil, err
+ }
+ err = connectionHelper.Delete(connection)
+ return &core.ApiResourceOutput{Body: connection}, err
+}
+
+/*
+GET /plugins/feishu/connections
+*/
+func ListConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput,
error) {
+ var connections []models.FeishuConnection
+ err := connectionHelper.List(&connections)
+ if err != nil {
+ return nil, err
+ }
+
+ return &core.ApiResourceOutput{Body: connections}, nil
+}
+
+/*
+GET /plugins/feishu/connections/:connectionId
+*/
+func GetConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput,
error) {
+ connection := &models.FeishuConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connection}, err
+}
diff --git a/plugins/feishu/tasks/task_data.go b/plugins/feishu/api/init.go
similarity index 63%
copy from plugins/feishu/tasks/task_data.go
copy to plugins/feishu/api/init.go
index aad0549d..6774e148 100644
--- a/plugins/feishu/tasks/task_data.go
+++ b/plugins/feishu/api/init.go
@@ -15,24 +15,25 @@ See the License for the specific language governing
permissions and
limitations under the License.
*/
-package tasks
+package api
import (
- "github.com/apache/incubator-devlake/plugins/feishu/models"
+ "github.com/apache/incubator-devlake/plugins/core"
"github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/go-playground/validator/v10"
+ "github.com/spf13/viper"
+ "gorm.io/gorm"
)
-type FeishuApiParams struct {
- ApiResName string `json:"apiResName"`
-}
-
-type FeishuOptions struct {
- NumOfDaysToCollect float64 `json:"numOfDaysToCollect"`
- Tasks []string `json:"tasks,omitempty"`
-}
+var vld *validator.Validate
+var connectionHelper *helper.ConnectionApiHelper
+var basicRes core.BasicRes
-type FeishuTaskData struct {
- Options *FeishuOptions
- ApiClient *helper.ApiAsyncClient
- FeishuMeetingTopUserItem *models.FeishuMeetingTopUserItem
+func Init(config *viper.Viper, logger core.Logger, database *gorm.DB) {
+ basicRes = helper.NewDefaultBasicRes(config, logger, database)
+ vld = validator.New()
+ connectionHelper = helper.NewConnectionHelper(
+ basicRes,
+ vld,
+ )
}
diff --git a/plugins/feishu/models/migrationscripts/register.go
b/plugins/feishu/apimodels/access_token.go
similarity index 66%
copy from plugins/feishu/models/migrationscripts/register.go
copy to plugins/feishu/apimodels/access_token.go
index 57d8ea48..d61f2bf9 100644
--- a/plugins/feishu/models/migrationscripts/register.go
+++ b/plugins/feishu/apimodels/access_token.go
@@ -15,15 +15,17 @@ See the License for the specific language governing
permissions and
limitations under the License.
*/
-package migrationscripts
+package apimodels
-import (
- "github.com/apache/incubator-devlake/migration"
-)
+type ApiAccessTokenRequest struct {
+ AppId string `json:"app_id"`
+ AppSecret string `json:"app_secret"`
+}
-// All return all the migration scripts
-func All() []migration.Script {
- return []migration.Script{
- new(InitSchemas), new(UpdateSchemas20220524),
- }
+type ApiAccessTokenResponse struct {
+ Code int `json:"code"`
+ Msg string `json:"msg"`
+ AppAccessToken string `json:"app_access_token"`
+ TenantAccessToken string `json:"tenant_access_token"`
+ Expire int `json:"expire"`
}
diff --git a/plugins/feishu/e2e/meeting_test.go
b/plugins/feishu/e2e/meeting_test.go
new file mode 100644
index 00000000..a2d3e7c9
--- /dev/null
+++ b/plugins/feishu/e2e/meeting_test.go
@@ -0,0 +1,61 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "testing"
+
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/feishu/impl"
+ "github.com/apache/incubator-devlake/plugins/feishu/models"
+ "github.com/apache/incubator-devlake/plugins/feishu/tasks"
+)
+
+func TestMeetingDataFlow(t *testing.T) {
+ var plugin impl.Feishu
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "feishu", plugin)
+
+ taskData := &tasks.FeishuTaskData{
+ Options: &tasks.FeishuOptions{
+ ConnectionId: 1,
+ },
+ }
+
+ // import raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_feishu_meeting_top_user_item.csv",
"_raw_feishu_meeting_top_user_item")
+
+ // verify extraction
+ dataflowTester.FlushTabler(&models.FeishuMeetingTopUserItem{})
+ dataflowTester.Subtask(tasks.ExtractMeetingTopUserItemMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.FeishuMeetingTopUserItem{},
+ "./snapshot_tables/_tool_feishu_meeting_top_user_items.csv",
+ []string{
+ "connection_id",
+ "start_time",
+ "name",
+ "meeting_count",
+ "meeting_duration",
+ "user_type",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+}
diff --git
a/plugins/feishu/e2e/raw_tables/_raw_feishu_meeting_top_user_item.csv
b/plugins/feishu/e2e/raw_tables/_raw_feishu_meeting_top_user_item.csv
new file mode 100644
index 00000000..9e64c15c
--- /dev/null
+++ b/plugins/feishu/e2e/raw_tables/_raw_feishu_meeting_top_user_item.csv
@@ -0,0 +1,29 @@
+id,params,data,url,input,created_at
+1,"{""connectionId"":1}","{""id"":""ou_e1ed6bb5eb13cad38cfa8531d59cde11"",""meeting_count"":""9"",""meeting_duration"":""256"",""name"":""用户A"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+2,"{""connectionId"":1}","{""id"":""ou_f72a1e52175e2a1e19bcae48af44d2ed"",""meeting_count"":""7"",""meeting_duration"":""167"",""name"":""用户B"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+3,"{""connectionId"":1}","{""id"":""ou_78e637b7fc8c614741e412c55e65f46e"",""meeting_count"":""9"",""meeting_duration"":""161"",""name"":""用户C"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+4,"{""connectionId"":1}","{""id"":""ou_eb39e98fe1cee6ee280274f393242caa"",""meeting_count"":""8"",""meeting_duration"":""151"",""name"":""用户D"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+5,"{""connectionId"":1}","{""id"":""ou_1754a884a17660b90d9b469e409b5d49"",""meeting_count"":""11"",""meeting_duration"":""136"",""name"":""用户E"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+6,"{""connectionId"":1}","{""id"":""ou_da0778cf463408f2d66ec13223d4982c"",""meeting_count"":""5"",""meeting_duration"":""126"",""name"":""用户F"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+7,"{""connectionId"":1}","{""id"":""ou_0d78dea7baacc61b47b4ddbc89f06e98"",""meeting_count"":""5"",""meeting_duration"":""110"",""name"":""用户G"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+8,"{""connectionId"":1}","{""id"":""ou_fb0302221f0f37d2edf83083908b940a"",""meeting_count"":""3"",""meeting_duration"":""109"",""name"":""用户H"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+9,"{""connectionId"":1}","{""id"":""ou_e96190d85dd4356083b85d128e5ee6a8"",""meeting_count"":""3"",""meeting_duration"":""104"",""name"":""用户I"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+10,"{""connectionId"":1}","{""id"":""ou_29dc478a9360719ccc5b820403cec5b5"",""meeting_count"":""7"",""meeting_duration"":""102"",""name"":""用户J"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+11,"{""connectionId"":1}","{""id"":""ou_5bfe3a304fc59abf04fae8933d94a918"",""meeting_count"":""6"",""meeting_duration"":""102"",""name"":""用户K"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+12,"{""connectionId"":1}","{""id"":""ou_adcedb2047821324708d95da060d622d"",""meeting_count"":""2"",""meeting_duration"":""97"",""name"":""用户L"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+13,"{""connectionId"":1}","{""id"":""ou_f519f036e4a8cedd68be360c7f994343"",""meeting_count"":""4"",""meeting_duration"":""97"",""name"":""用户M"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+14,"{""connectionId"":1}","{""id"":""ou_642d3c32689dd63de1ac1c9c841b7eb8"",""meeting_count"":""2"",""meeting_duration"":""96"",""name"":""用户N"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655856000&limit=100&order_by=2&start_time=1655769600,"{""PairEndTime"":
""2022-06-22T08:00:00+08:00"", ""PairStartTime"":
""2022-06-21T08:00:00+08:00""}",2022-06-22 06:24:43.912949+00:00
+70,"{""connectionId"":1}","{""id"":""ou_e1ed6bb5eb13cad38cfa8531d59cde11"",""meeting_count"":""21"",""meeting_duration"":""706"",""name"":""用户A"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+71,"{""connectionId"":1}","{""id"":""ou_fb0302221f0f37d2edf83083908b940a"",""meeting_count"":""11"",""meeting_duration"":""456"",""name"":""用户H"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+72,"{""connectionId"":1}","{""id"":""ou_f72a1e52175e2a1e19bcae48af44d2ed"",""meeting_count"":""13"",""meeting_duration"":""417"",""name"":""用户B"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+73,"{""connectionId"":1}","{""id"":""ou_d5d7f2df8148ae282544c4c4ad7e0fe0"",""meeting_count"":""9"",""meeting_duration"":""417"",""name"":""用户O"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+74,"{""connectionId"":1}","{""id"":""ou_1754a884a17660b90d9b469e409b5d49"",""meeting_count"":""17"",""meeting_duration"":""377"",""name"":""用户E"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+75,"{""connectionId"":1}","{""id"":""ou_f519f036e4a8cedd68be360c7f994343"",""meeting_count"":""12"",""meeting_duration"":""307"",""name"":""用户M"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+76,"{""connectionId"":1}","{""id"":""ou_32c9b4db9247016c8fd4d45647b39074"",""meeting_count"":""10"",""meeting_duration"":""304"",""name"":""用户P"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+77,"{""connectionId"":1}","{""id"":""ou_5bfe3a304fc59abf04fae8933d94a918"",""meeting_count"":""18"",""meeting_duration"":""303"",""name"":""用户K"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+78,"{""connectionId"":1}","{""id"":""ou_e08fc867d0a617e6f681aaa39eb9645a"",""meeting_count"":""9"",""meeting_duration"":""297"",""name"":""用户Q"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+79,"{""connectionId"":1}","{""id"":""ou_9d3231bd1d159dc68ff0dc601d21a116"",""meeting_count"":""9"",""meeting_duration"":""292"",""name"":""用户R"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+80,"{""connectionId"":1}","{""id"":""ou_2f60a3ad2dc0eab21c78a3cf89ff3ddd"",""meeting_count"":""5"",""meeting_duration"":""275"",""name"":""用户S"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+81,"{""connectionId"":1}","{""id"":""ou_133e7e5ff4df17f8484dfc94ff20a1af"",""meeting_count"":""9"",""meeting_duration"":""272"",""name"":""用户T"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+82,"{""connectionId"":1}","{""id"":""ou_5610f62404011e60446e7fa7ffe4f992"",""meeting_count"":""8"",""meeting_duration"":""265"",""name"":""用户U"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
+83,"{""connectionId"":1}","{""id"":""ou_19c2a0177456027e6c475d4bb948f680"",""meeting_count"":""10"",""meeting_duration"":""262"",""name"":""用户V"",""user_type"":1}",https://open.feishu.cn/open-apis/vc/v1/reports/get_top_user?end_time=1655769600&limit=100&order_by=2&start_time=1655683200,"{""PairEndTime"":
""2022-06-21T08:00:00+08:00"", ""PairStartTime"":
""2022-06-20T08:00:00+08:00""}",2022-06-22 06:24:44.399961+00:00
diff --git
a/plugins/feishu/e2e/snapshot_tables/_tool_feishu_meeting_top_user_items.csv
b/plugins/feishu/e2e/snapshot_tables/_tool_feishu_meeting_top_user_items.csv
new file mode 100644
index 00000000..e71be400
--- /dev/null
+++ b/plugins/feishu/e2e/snapshot_tables/_tool_feishu_meeting_top_user_items.csv
@@ -0,0 +1,29 @@
+connection_id,start_time,name,meeting_count,meeting_duration,user_type,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,2022-06-20T00:00:00.000+00:00,用户A,21,706,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,70,
+1,2022-06-20T00:00:00.000+00:00,用户B,13,417,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,72,
+1,2022-06-20T00:00:00.000+00:00,用户E,17,377,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,74,
+1,2022-06-20T00:00:00.000+00:00,用户H,11,456,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,71,
+1,2022-06-20T00:00:00.000+00:00,用户K,18,303,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,77,
+1,2022-06-20T00:00:00.000+00:00,用户M,12,307,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,75,
+1,2022-06-20T00:00:00.000+00:00,用户O,9,417,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,73,
+1,2022-06-20T00:00:00.000+00:00,用户P,10,304,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,76,
+1,2022-06-20T00:00:00.000+00:00,用户Q,9,297,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,78,
+1,2022-06-20T00:00:00.000+00:00,用户R,9,292,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,79,
+1,2022-06-20T00:00:00.000+00:00,用户S,5,275,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,80,
+1,2022-06-20T00:00:00.000+00:00,用户T,9,272,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,81,
+1,2022-06-20T00:00:00.000+00:00,用户U,8,265,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,82,
+1,2022-06-20T00:00:00.000+00:00,用户V,10,262,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,83,
+1,2022-06-21T00:00:00.000+00:00,用户A,9,256,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,1,
+1,2022-06-21T00:00:00.000+00:00,用户B,7,167,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,2,
+1,2022-06-21T00:00:00.000+00:00,用户C,9,161,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,3,
+1,2022-06-21T00:00:00.000+00:00,用户D,8,151,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,4,
+1,2022-06-21T00:00:00.000+00:00,用户E,11,136,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,5,
+1,2022-06-21T00:00:00.000+00:00,用户F,5,126,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,6,
+1,2022-06-21T00:00:00.000+00:00,用户G,5,110,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,7,
+1,2022-06-21T00:00:00.000+00:00,用户H,3,109,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,8,
+1,2022-06-21T00:00:00.000+00:00,用户I,3,104,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,9,
+1,2022-06-21T00:00:00.000+00:00,用户J,7,102,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,10,
+1,2022-06-21T00:00:00.000+00:00,用户K,6,102,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,11,
+1,2022-06-21T00:00:00.000+00:00,用户L,2,97,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,12,
+1,2022-06-21T00:00:00.000+00:00,用户M,4,97,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,13,
+1,2022-06-21T00:00:00.000+00:00,用户N,2,96,1,"{""connectionId"":1}",_raw_feishu_meeting_top_user_item,14,
diff --git a/plugins/feishu/feishu.go b/plugins/feishu/feishu.go
index 1bce30e9..7ee8b3d3 100644
--- a/plugins/feishu/feishu.go
+++ b/plugins/feishu/feishu.go
@@ -18,79 +18,25 @@ limitations under the License.
package main
import (
- "github.com/apache/incubator-devlake/migration"
- "github.com/apache/incubator-devlake/plugins/core"
-
"github.com/apache/incubator-devlake/plugins/feishu/models/migrationscripts"
- "github.com/apache/incubator-devlake/plugins/feishu/tasks"
+ "github.com/apache/incubator-devlake/plugins/feishu/impl"
"github.com/apache/incubator-devlake/runner"
- "github.com/mitchellh/mapstructure"
"github.com/spf13/cobra"
- "github.com/spf13/viper"
- "gorm.io/gorm"
)
-var _ core.PluginMeta = (*Feishu)(nil)
-var _ core.PluginInit = (*Feishu)(nil)
-var _ core.PluginTask = (*Feishu)(nil)
-var _ core.PluginApi = (*Feishu)(nil)
-var _ core.Migratable = (*Feishu)(nil)
-
-type Feishu struct{}
-
-func (plugin Feishu) Init(config *viper.Viper, logger core.Logger, db
*gorm.DB) error {
- return nil
-}
-
-func (plugin Feishu) Description() string {
- return "To collect and enrich data from Feishu"
-}
-
-func (plugin Feishu) SubTaskMetas() []core.SubTaskMeta {
- return []core.SubTaskMeta{
- tasks.CollectMeetingTopUserItemMeta,
- tasks.ExtractMeetingTopUserItemMeta,
- }
-}
-
-func (plugin Feishu) PrepareTaskData(taskCtx core.TaskContext, options
map[string]interface{}) (interface{}, error) {
- var op tasks.FeishuOptions
- err := mapstructure.Decode(options, &op)
- if err != nil {
- return nil, err
- }
- apiClient, err := tasks.NewFeishuApiClient(taskCtx)
- if err != nil {
- return nil, err
- }
- return &tasks.FeishuTaskData{
- Options: &op,
- ApiClient: apiClient,
- }, nil
-}
-
-func (plugin Feishu) RootPkgPath() string {
- return "github.com/apache/incubator-devlake/plugins/feishu"
-}
-
-func (plugin Feishu) MigrationScripts() []migration.Script {
- return migrationscripts.All()
-}
-
-func (plugin Feishu) ApiResources()
map[string]map[string]core.ApiResourceHandler {
- return map[string]map[string]core.ApiResourceHandler{}
-}
-
-var PluginEntry Feishu
+var PluginEntry impl.Feishu
// standalone mode for debugging
func main() {
- feishuCmd := &cobra.Command{Use: "feishu"}
- numOfDaysToCollect := feishuCmd.Flags().IntP("numOfDaysToCollect", "n",
8, "feishu collect days")
- _ = feishuCmd.MarkFlagRequired("numOfDaysToCollect")
- feishuCmd.Run = func(cmd *cobra.Command, args []string) {
+ cmd := &cobra.Command{Use: "feishu"}
+ connectionId := cmd.Flags().Uint64P("connectionId", "c", 0, "feishu
connection id")
+ numOfDaysToCollect := cmd.Flags().IntP("numOfDaysToCollect", "n", 8,
"feishu collect days")
+ _ = cmd.MarkFlagRequired("connectionId")
+ _ = cmd.MarkFlagRequired("numOfDaysToCollect")
+ cmd.Run = func(cmd *cobra.Command, args []string) {
runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
+ "connectionId": *connectionId,
"numOfDaysToCollect": *numOfDaysToCollect,
})
}
- runner.RunCmd(feishuCmd)
+ runner.RunCmd(cmd)
}
diff --git a/plugins/feishu/feishu.go b/plugins/feishu/impl/impl.go
similarity index 62%
copy from plugins/feishu/feishu.go
copy to plugins/feishu/impl/impl.go
index 1bce30e9..cc4a4e03 100644
--- a/plugins/feishu/feishu.go
+++ b/plugins/feishu/impl/impl.go
@@ -15,18 +15,20 @@ See the License for the specific language governing
permissions and
limitations under the License.
*/
-package main
+package impl
import (
+ "github.com/mitchellh/mapstructure"
+ "github.com/spf13/viper"
+ "gorm.io/gorm"
+
"github.com/apache/incubator-devlake/migration"
"github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/feishu/api"
+ "github.com/apache/incubator-devlake/plugins/feishu/models"
"github.com/apache/incubator-devlake/plugins/feishu/models/migrationscripts"
"github.com/apache/incubator-devlake/plugins/feishu/tasks"
- "github.com/apache/incubator-devlake/runner"
- "github.com/mitchellh/mapstructure"
- "github.com/spf13/cobra"
- "github.com/spf13/viper"
- "gorm.io/gorm"
+ "github.com/apache/incubator-devlake/plugins/helper"
)
var _ core.PluginMeta = (*Feishu)(nil)
@@ -38,6 +40,36 @@ var _ core.Migratable = (*Feishu)(nil)
type Feishu struct{}
func (plugin Feishu) Init(config *viper.Viper, logger core.Logger, db
*gorm.DB) error {
+ api.Init(config, logger, db)
+
+ // FIXME after config-ui support feishu plugin
+ // save env to db where name=feishu
+ connection := &models.FeishuConnection{}
+ if db.Migrator().HasTable(connection) {
+ err := db.Find(connection, map[string]string{"name":
"Feishu"}).Error
+ if err != nil {
+ return err
+ }
+ if connection.ID != 0 {
+ encodeKey := config.GetString(core.EncodeKeyEnvStr)
+ connection.Endpoint =
config.GetString(`FEISHU_ENDPOINT`)
+ connection.AppId = config.GetString(`FEISHU_APPID`)
+ connection.SecretKey =
config.GetString(`FEISHU_APPSCRECT`)
+ if connection.Endpoint != `` && connection.AppId != ``
&& connection.SecretKey != `` && encodeKey != `` {
+ err = helper.UpdateEncryptFields(connection,
func(plaintext string) (string, error) {
+ return core.Encrypt(encodeKey,
plaintext)
+ })
+ if err != nil {
+ return err
+ }
+ // update from .env and save to db
+ err = db.Updates(connection).Error
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
return nil
}
@@ -58,7 +90,18 @@ func (plugin Feishu) PrepareTaskData(taskCtx
core.TaskContext, options map[strin
if err != nil {
return nil, err
}
- apiClient, err := tasks.NewFeishuApiClient(taskCtx)
+
+ connectionHelper := helper.NewConnectionHelper(
+ taskCtx,
+ nil,
+ )
+ connection := &models.FeishuConnection{}
+ err = connectionHelper.FirstById(connection, op.ConnectionId)
+ if err != nil {
+ return nil, err
+ }
+
+ apiClient, err := tasks.NewFeishuApiClient(taskCtx, connection)
if err != nil {
return nil, err
}
@@ -79,18 +122,3 @@ func (plugin Feishu) MigrationScripts() []migration.Script {
func (plugin Feishu) ApiResources()
map[string]map[string]core.ApiResourceHandler {
return map[string]map[string]core.ApiResourceHandler{}
}
-
-var PluginEntry Feishu
-
-// standalone mode for debugging
-func main() {
- feishuCmd := &cobra.Command{Use: "feishu"}
- numOfDaysToCollect := feishuCmd.Flags().IntP("numOfDaysToCollect", "n",
8, "feishu collect days")
- _ = feishuCmd.MarkFlagRequired("numOfDaysToCollect")
- feishuCmd.Run = func(cmd *cobra.Command, args []string) {
- runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
- "numOfDaysToCollect": *numOfDaysToCollect,
- })
- }
- runner.RunCmd(feishuCmd)
-}
diff --git a/plugins/feishu/models/meeting_top_user_item.go
b/plugins/feishu/models/connection.go
similarity index 58%
copy from plugins/feishu/models/meeting_top_user_item.go
copy to plugins/feishu/models/connection.go
index 9b50c0c0..b721e7af 100644
--- a/plugins/feishu/models/meeting_top_user_item.go
+++ b/plugins/feishu/models/connection.go
@@ -17,20 +17,20 @@ limitations under the License.
package models
-import (
- "github.com/apache/incubator-devlake/models/common"
- "time"
-)
+import "github.com/apache/incubator-devlake/plugins/helper"
-type FeishuMeetingTopUserItem struct {
- common.NoPKModel `json:"-"`
- StartTime time.Time `gorm:"primaryKey"`
- Name string `json:"name"
gorm:"primaryKey;type:varchar(255)"`
- MeetingCount string `json:"meeting_count"
gorm:"type:varchar(255)"`
- MeetingDuration string `json:"meeting_duration"
gorm:"type:varchar(255)"`
- UserType int64 `json:"user_type"`
+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"`
}
-func (FeishuMeetingTopUserItem) TableName() string {
- return "_tool_feishu_meeting_top_user_items"
+type FeishuConnection struct {
+ helper.RestConnection `mapstructure:",squash"`
+ helper.AppKey `mapstructure:",squash"`
+}
+
+func (FeishuConnection) TableName() string {
+ return "_tool_feishu_connections"
}
diff --git a/plugins/feishu/models/meeting_top_user_item.go
b/plugins/feishu/models/meeting_top_user_item.go
index 9b50c0c0..01625590 100644
--- a/plugins/feishu/models/meeting_top_user_item.go
+++ b/plugins/feishu/models/meeting_top_user_item.go
@@ -24,6 +24,7 @@ import (
type FeishuMeetingTopUserItem struct {
common.NoPKModel `json:"-"`
+ ConnectionId uint64 `gorm:"primaryKey"`
StartTime time.Time `gorm:"primaryKey"`
Name string `json:"name"
gorm:"primaryKey;type:varchar(255)"`
MeetingCount string `json:"meeting_count"
gorm:"type:varchar(255)"`
diff --git a/plugins/feishu/models/migrationscripts/init_schema.go
b/plugins/feishu/models/migrationscripts/archived/connection.go
similarity index 53%
copy from plugins/feishu/models/migrationscripts/init_schema.go
copy to plugins/feishu/models/migrationscripts/archived/connection.go
index 6f6cd06a..8e8e695f 100644
--- a/plugins/feishu/models/migrationscripts/init_schema.go
+++ b/plugins/feishu/models/migrationscripts/archived/connection.go
@@ -15,27 +15,22 @@ See the License for the specific language governing
permissions and
limitations under the License.
*/
-package migrationscripts
+package archived
import (
- "context"
-
-
"github.com/apache/incubator-devlake/plugins/feishu/models/migrationscripts/archived"
- "gorm.io/gorm"
+ commonArchived
"github.com/apache/incubator-devlake/models/migrationscripts/archived"
)
-type InitSchemas struct{}
-
-func (*InitSchemas) Up(ctx context.Context, db *gorm.DB) error {
- return db.Migrator().AutoMigrate(
- &archived.FeishuMeetingTopUserItem{},
- )
-}
-
-func (*InitSchemas) Version() uint64 {
- return 20220407201134
+type FeishuConnection struct {
+ commonArchived.Model
+ Name string `gorm:"type:varchar(100);uniqueIndex" json:"name"
validate:"required"`
+ Endpoint string `mapstructure:"endpoint" env:"GITHUB_ENDPOINT"
validate:"required"`
+ Proxy string `mapstructure:"proxy" env:"GITHUB_PROXY"`
+ RateLimit int `comment:"api request rate limit per hour"`
+ AppId string `mapstructure:"app_id" validate:"required"
json:"app_id"`
+ SecretKey string `mapstructure:"secret_key" validate:"required"
json:"secret_key" encrypt:"yes"`
}
-func (*InitSchemas) Name() string {
- return "Feishu init schemas"
+func (FeishuConnection) TableName() string {
+ return "_tool_feishu_connections"
}
diff --git
a/plugins/feishu/models/migrationscripts/archived/meeting_top_user_item.go
b/plugins/feishu/models/migrationscripts/archived/meeting_top_user_item.go
index 149a6da9..733f9897 100644
--- a/plugins/feishu/models/migrationscripts/archived/meeting_top_user_item.go
+++ b/plugins/feishu/models/migrationscripts/archived/meeting_top_user_item.go
@@ -18,19 +18,18 @@ limitations under the License.
package archived
import (
+ "github.com/apache/incubator-devlake/models/common"
"time"
-
- "github.com/apache/incubator-devlake/models/migrationscripts/archived"
)
type FeishuMeetingTopUserItem struct {
- archived.Model `json:"-"`
- StartTime time.Time
- MeetingCount string `json:"meeting_count" gorm:"type:varchar(255)"`
- MeetingDuration string `json:"meeting_duration"
gorm:"type:varchar(255)"`
- Name string `json:"name" gorm:"type:varchar(255)"`
- UserType int64 `json:"user_type"`
- archived.RawDataOrigin
+ common.NoPKModel `json:"-"`
+ ConnectionId uint64 `gorm:"primaryKey"`
+ StartTime time.Time `gorm:"primaryKey"`
+ Name string `json:"name"
gorm:"primaryKey;type:varchar(255)"`
+ MeetingCount string `json:"meeting_count"
gorm:"type:varchar(255)"`
+ MeetingDuration string `json:"meeting_duration"
gorm:"type:varchar(255)"`
+ UserType int64 `json:"user_type"`
}
func (FeishuMeetingTopUserItem) TableName() string {
diff --git a/plugins/feishu/models/migrationscripts/init_schema.go
b/plugins/feishu/models/migrationscripts/init_schema.go
index 6f6cd06a..020ff6d8 100644
--- a/plugins/feishu/models/migrationscripts/init_schema.go
+++ b/plugins/feishu/models/migrationscripts/init_schema.go
@@ -19,21 +19,58 @@ package migrationscripts
import (
"context"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
"github.com/apache/incubator-devlake/plugins/feishu/models/migrationscripts/archived"
"gorm.io/gorm"
)
-type InitSchemas struct{}
+type InitSchemas struct {
+ config core.ConfigGetter
+}
+
+func (u *InitSchemas) SetConfigGetter(config core.ConfigGetter) {
+ u.config = config
+}
-func (*InitSchemas) Up(ctx context.Context, db *gorm.DB) error {
- return db.Migrator().AutoMigrate(
+func (u *InitSchemas) Up(ctx context.Context, db *gorm.DB) error {
+ err := db.Migrator().DropTable(
+ &archived.FeishuConnection{},
&archived.FeishuMeetingTopUserItem{},
)
+ if err != nil {
+ return err
+ }
+ err = db.Migrator().CreateTable(
+ &archived.FeishuConnection{},
+ &archived.FeishuMeetingTopUserItem{},
+ )
+ if err != nil {
+ return err
+ }
+
+ encodeKey := u.config.GetString(core.EncodeKeyEnvStr)
+ connection := &archived.FeishuConnection{}
+ connection.Endpoint = u.config.GetString(`FEISHU_ENDPOINT`)
+ connection.AppId = u.config.GetString(`FEISHU_APPID`)
+ connection.SecretKey = u.config.GetString(`FEISHU_APPSCRECT`)
+ connection.Name = `Feishu`
+ if connection.Endpoint != `` && connection.AppId != `` &&
connection.SecretKey != `` && encodeKey != `` {
+ err = helper.UpdateEncryptFields(connection, func(plaintext
string) (string, error) {
+ return core.Encrypt(encodeKey, plaintext)
+ })
+ if err != nil {
+ return err
+ }
+ // update from .env and save to db
+ db.Create(connection)
+ }
+ return nil
}
func (*InitSchemas) Version() uint64 {
- return 20220407201134
+ return 20220620000001
}
func (*InitSchemas) Name() string {
diff --git a/plugins/feishu/models/migrationscripts/register.go
b/plugins/feishu/models/migrationscripts/register.go
index 57d8ea48..06d924d0 100644
--- a/plugins/feishu/models/migrationscripts/register.go
+++ b/plugins/feishu/models/migrationscripts/register.go
@@ -24,6 +24,6 @@ import (
// All return all the migration scripts
func All() []migration.Script {
return []migration.Script{
- new(InitSchemas), new(UpdateSchemas20220524),
+ new(InitSchemas),
}
}
diff --git a/plugins/feishu/models/migrationscripts/updateSchemas20220526.go
b/plugins/feishu/models/migrationscripts/updateSchemas20220526.go
deleted file mode 100644
index 634046d4..00000000
--- a/plugins/feishu/models/migrationscripts/updateSchemas20220526.go
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements. See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package migrationscripts
-
-import (
- "context"
- "github.com/apache/incubator-devlake/models/common"
-
"github.com/apache/incubator-devlake/plugins/feishu/models/migrationscripts/archived"
- "gorm.io/gorm/clause"
- "time"
-
- "gorm.io/gorm"
-)
-
-type FeishuMeetingTopUserItem20220524Temp struct {
- common.NoPKModel `json:"-"`
- StartTime time.Time `gorm:"primaryKey"`
- Name string `json:"name"
gorm:"primaryKey;type:varchar(255)"`
- MeetingCount string `json:"meeting_count"
gorm:"type:varchar(255)"`
- MeetingDuration string `json:"meeting_duration"
gorm:"type:varchar(255)"`
- UserType int64 `json:"user_type"`
-}
-
-func (FeishuMeetingTopUserItem20220524Temp) TableName() string {
- return "_tool_feishu_meeting_top_user_items_tmp"
-}
-
-type FeishuMeetingTopUserItem20220524 struct {
-}
-
-func (FeishuMeetingTopUserItem20220524) TableName() string {
- return "_tool_feishu_meeting_top_user_items"
-}
-
-type UpdateSchemas20220524 struct{}
-
-func (*UpdateSchemas20220524) Up(ctx context.Context, db *gorm.DB) error {
- cursor, err := db.Model(archived.FeishuMeetingTopUserItem{}).Rows()
- if err != nil {
- return err
- }
- defer cursor.Close()
- // 1. create a temporary table to store unique records
- err = db.Migrator().CreateTable(FeishuMeetingTopUserItem20220524Temp{})
- if err != nil {
- return err
- }
- // 2. dedupe records and insert into the temporary table
- for cursor.Next() {
- inputRow := FeishuMeetingTopUserItem20220524Temp{}
- err := db.ScanRows(cursor, &inputRow)
- if err != nil {
- return err
- }
- err = db.Clauses(clause.OnConflict{UpdateAll:
true}).Create(inputRow).Error
- if err != nil {
- return err
- }
- }
- // 3. drop old table
- err = db.Migrator().DropTable(archived.FeishuMeetingTopUserItem{})
- if err != nil {
- return err
- }
- // 4. rename the temporary table to the old table
- return
db.Migrator().RenameTable(FeishuMeetingTopUserItem20220524Temp{},
FeishuMeetingTopUserItem20220524{})
-}
-
-func (*UpdateSchemas20220524) Version() uint64 {
- return 20220524000001
-}
-
-func (*UpdateSchemas20220524) Name() string {
- return "change primary column `id` to start_time+name"
-}
diff --git a/plugins/feishu/tasks/api_client.go
b/plugins/feishu/tasks/api_client.go
index da54b5f9..941403a4 100644
--- a/plugins/feishu/tasks/api_client.go
+++ b/plugins/feishu/tasks/api_client.go
@@ -19,60 +19,33 @@ package tasks
import (
"fmt"
+ "github.com/apache/incubator-devlake/plugins/feishu/apimodels"
+ "github.com/apache/incubator-devlake/plugins/feishu/models"
"net/http"
"github.com/apache/incubator-devlake/plugins/core"
"github.com/apache/incubator-devlake/plugins/helper"
- "github.com/apache/incubator-devlake/utils"
)
-type ApiAccessTokenRequest struct {
- AppId string `json:"app_id"`
- AppSecret string `json:"app_secret"`
-}
-
-type ApiAccessTokenResponse struct {
- Code int `json:"code"`
- Msg string `json:"msg"`
- AppAccessToken string `json:"app_access_token"`
- TenantAccessToken string `json:"tenant_access_token"`
- Expire int `json:"expire"`
-}
-
const AUTH_ENDPOINT = "https://open.feishu.cn"
const ENDPOINT = "https://open.feishu.cn/open-apis/vc/v1"
-func NewFeishuApiClient(taskCtx core.TaskContext) (*helper.ApiAsyncClient,
error) {
- // load and process cconfiguration
- appId := taskCtx.GetConfig("FEISHU_APPID")
- if appId == "" {
- return nil, fmt.Errorf("invalid FEISHU_APPID")
- }
- secretKey := taskCtx.GetConfig("FEISHU_APPSCRECT")
- if secretKey == "" {
- return nil, fmt.Errorf("invalid FEISHU_APPSCRECT")
- }
- userRateLimit, err :=
utils.StrToIntOr(taskCtx.GetConfig("FEISHU_API_REQUESTS_PER_HOUR"), 18000)
- if err != nil {
- return nil, err
- }
- proxy := taskCtx.GetConfig("FEISHU_PROXY")
-
- authApiClient, err := helper.NewApiClient(AUTH_ENDPOINT, nil, 0, proxy,
taskCtx.GetContext())
+func NewFeishuApiClient(taskCtx core.TaskContext, connection
*models.FeishuConnection) (*helper.ApiAsyncClient, error) {
+ authApiClient, err := helper.NewApiClient(AUTH_ENDPOINT, nil, 0,
connection.Proxy, taskCtx.GetContext())
if err != nil {
return nil, err
}
// request for access token
- tokenReqBody := &ApiAccessTokenRequest{
- AppId: appId,
- AppSecret: secretKey,
+ 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 := &ApiAccessTokenResponse{}
+ tokenResBody := &apimodels.ApiAccessTokenResponse{}
err = helper.UnmarshalResponse(tokenRes, tokenResBody)
if err != nil {
return nil, err
@@ -81,7 +54,7 @@ func NewFeishuApiClient(taskCtx core.TaskContext)
(*helper.ApiAsyncClient, error
return nil, fmt.Errorf("failed to request access token")
}
// real request apiClient
- apiClient, err := helper.NewApiClient(ENDPOINT, nil, 0, proxy,
taskCtx.GetContext())
+ apiClient, err := helper.NewApiClient(ENDPOINT, nil, 0,
connection.Proxy, taskCtx.GetContext())
if err != nil {
return nil, err
}
@@ -99,7 +72,7 @@ func NewFeishuApiClient(taskCtx core.TaskContext)
(*helper.ApiAsyncClient, error
// create async api client
asyncApiCLient, err := helper.CreateAsyncApiClient(taskCtx, apiClient,
&helper.ApiRateLimitCalculator{
- UserRateLimitPerHour: userRateLimit,
+ UserRateLimitPerHour: connection.RateLimit,
})
if err != nil {
return nil, err
diff --git a/plugins/feishu/tasks/meeting_top_user_item_collector.go
b/plugins/feishu/tasks/meeting_top_user_item_collector.go
index 4b8ce214..977a9eb5 100644
--- a/plugins/feishu/tasks/meeting_top_user_item_collector.go
+++ b/plugins/feishu/tasks/meeting_top_user_item_collector.go
@@ -40,18 +40,17 @@ func CollectMeetingTopUserItem(taskCtx core.SubTaskContext)
error {
if err != nil {
return err
}
- incremental := false
collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: FeishuApiParams{
- ApiResName: "top_user_report",
+ ConnectionId: data.Options.ConnectionId,
},
Table: RAW_MEETING_TOP_USER_ITEM_TABLE,
},
ApiClient: data.ApiClient,
- Incremental: incremental,
+ Incremental: false,
Input: iterator,
UrlTemplate: "/reports/get_top_user",
Query: func(reqData *helper.RequestData) (url.Values, error) {
diff --git a/plugins/feishu/tasks/meeting_top_user_item_extractor.go
b/plugins/feishu/tasks/meeting_top_user_item_extractor.go
index fd4c3ca5..6a704393 100644
--- a/plugins/feishu/tasks/meeting_top_user_item_extractor.go
+++ b/plugins/feishu/tasks/meeting_top_user_item_extractor.go
@@ -27,11 +27,12 @@ import (
var _ core.SubTaskEntryPoint = ExtractMeetingTopUserItem
func ExtractMeetingTopUserItem(taskCtx core.SubTaskContext) error {
+ data := taskCtx.GetData().(*FeishuTaskData)
extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: FeishuApiParams{
- ApiResName: "top_user_report",
+ ConnectionId: data.Options.ConnectionId,
},
Table: RAW_MEETING_TOP_USER_ITEM_TABLE,
},
@@ -48,7 +49,8 @@ func ExtractMeetingTopUserItem(taskCtx core.SubTaskContext)
error {
}
results := make([]interface{}, 0)
results = append(results,
&models.FeishuMeetingTopUserItem{
- StartTime:
rawInput.PairStartTime.AddDate(0, 0, -1),
+ ConnectionId: data.Options.ConnectionId,
+ StartTime: rawInput.PairStartTime,
MeetingCount: body.MeetingCount,
MeetingDuration: body.MeetingDuration,
Name: body.Name,
diff --git a/plugins/feishu/tasks/task_data.go
b/plugins/feishu/tasks/task_data.go
index aad0549d..a5d461db 100644
--- a/plugins/feishu/tasks/task_data.go
+++ b/plugins/feishu/tasks/task_data.go
@@ -23,12 +23,12 @@ import (
)
type FeishuApiParams struct {
- ApiResName string `json:"apiResName"`
+ ConnectionId uint64 `json:"connectionId"`
}
type FeishuOptions struct {
- NumOfDaysToCollect float64 `json:"numOfDaysToCollect"`
- Tasks []string `json:"tasks,omitempty"`
+ ConnectionId uint64 `json:"connectionId"`
+ NumOfDaysToCollect float64 `json:"numOfDaysToCollect"`
}
type FeishuTaskData struct {
diff --git a/plugins/tapd/e2e/snapshot_tables/issue_commits_story.csv
b/plugins/tapd/e2e/snapshot_tables/issue_commits_story.csv
new file mode 100644
index 00000000..9d9ded75
--- /dev/null
+++ b/plugins/tapd/e2e/snapshot_tables/issue_commits_story.csv
@@ -0,0 +1,2 @@
+issue_id,commit_sha,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+tapd:TapdIssue:1:11991001049945,testcommit,"{""ConnectionId"":1,""CompanyId"":99,""WorkspaceId"":991}",_raw_tapd_api_story_commits,1,