You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by li...@apache.org on 2023/03/24 01:31:18 UTC
[incubator-devlake] branch main updated: feat: add teambition plugin (#4704)
This is an automated email from the ASF dual-hosted git repository.
likyh 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 1e1a50a54 feat: add teambition plugin (#4704)
1e1a50a54 is described below
commit 1e1a50a54b64eff8213a2c4d9afc383cad40c4f2
Author: coldgust <11...@users.noreply.github.com>
AuthorDate: Fri Mar 24 09:31:11 2023 +0800
feat: add teambition plugin (#4704)
* feat: init teambition plugin
* feat: add task activities
* feat: add issue_worklogs support
* feat: add boards support
* feat: add board_issue & sprint_issue support
* feat: add board_sprint & sprint support
* feat: add task_flow_status & task_scenario support
* feat: add issue support
* feat: add blueprint support
* test: add e2e test support
* fix: remove incorrect use of timeAfter and incremental
* fix: optimize project collector and remove organization_id option
* fix: e2e test
---
backend/go.mod | 1 +
backend/go.sum | 3 +
backend/plugins/teambition/api/blueprint200.go | 107 ++
backend/plugins/teambition/api/connection.go | 163 ++++
backend/plugins/teambition/api/init.go | 37 +
backend/plugins/teambition/e2e/changelogs_test.go | 69 ++
backend/plugins/teambition/e2e/projects_test.go | 69 ++
.../raw_tables/_raw_teambition_api_projects.csv | 2 +
.../e2e/raw_tables/_raw_teambition_api_sprints.csv | 4 +
.../_raw_teambition_api_task_activities.csv | 64 ++
.../_raw_teambition_api_task_flow_status.csv | 31 +
.../_raw_teambition_api_task_scenarios.csv | 7 +
.../raw_tables/_raw_teambition_api_task_tags.csv | 8 +
.../_raw_teambition_api_task_worktime.csv | 3 +
.../e2e/raw_tables/_raw_teambition_api_tasks.csv | 23 +
.../snapshot_tables/_tool_teambition_accounts.csv | 2 +
.../snapshot_tables/_tool_teambition_projects.csv | 2 +
.../snapshot_tables/_tool_teambition_sprints.csv | 4 +
.../_tool_teambition_task_activities.csv | 64 ++
.../_tool_teambition_task_flow_status.csv | 31 +
.../_tool_teambition_task_scenarios.csv | 7 +
.../_tool_teambition_task_tag_tasks.csv | 9 +
.../snapshot_tables/_tool_teambition_task_tags.csv | 8 +
.../_tool_teambition_task_worktime.csv | 3 +
.../e2e/snapshot_tables/_tool_teambition_tasks.csv | 23 +
.../e2e/snapshot_tables/board_issues.csv | 23 +
.../e2e/snapshot_tables/board_sprints.csv | 4 +
.../teambition/e2e/snapshot_tables/boards.csv | 2 +
.../e2e/snapshot_tables/issue_changelogs.csv | 64 ++
.../e2e/snapshot_tables/issue_comments.csv | 5 +
.../e2e/snapshot_tables/issue_labels.csv | 9 +
.../e2e/snapshot_tables/issue_worklogs.csv | 3 +
.../teambition/e2e/snapshot_tables/issues.csv | 23 +
.../e2e/snapshot_tables/sprint_issues.csv | 11 +
.../teambition/e2e/snapshot_tables/sprints.csv | 4 +
backend/plugins/teambition/e2e/sprint_test.go | 69 ++
.../plugins/teambition/e2e/task_comment_test.go | 69 ++
.../plugins/teambition/e2e/task_scenarios_test.go | 65 ++
backend/plugins/teambition/e2e/task_tag_test.go | 74 ++
backend/plugins/teambition/e2e/task_test.go | 100 ++
.../teambition/e2e/task_work_flow_status_test.go | 65 ++
.../plugins/teambition/e2e/task_worktime_test.go | 78 ++
backend/plugins/teambition/impl/impl.go | 171 ++++
backend/plugins/teambition/models/account.go | 51 +
backend/plugins/teambition/models/connection.go | 62 ++
backend/plugins/teambition/models/input.go | 27 +
.../migrationscripts/20230314_add_init_tables.go | 52 +
.../teambition/models/migrationscripts/register.go | 27 +
backend/plugins/teambition/models/project.go | 61 ++
backend/plugins/teambition/models/sprint.go | 47 +
backend/plugins/teambition/models/task.go | 75 ++
backend/plugins/teambition/models/task_activity.go | 43 +
backend/plugins/teambition/models/task_comment.go | 27 +
.../plugins/teambition/models/task_flow_status.go | 45 +
backend/plugins/teambition/models/task_scenario.go | 53 +
backend/plugins/teambition/models/task_tag.go | 42 +
backend/plugins/teambition/models/task_tag_task.go | 34 +
backend/plugins/teambition/models/task_worktime.go | 46 +
.../plugins/teambition/tasks/account_collector.go | 65 ++
.../plugins/teambition/tasks/account_converter.go | 83 ++
.../plugins/teambition/tasks/account_extractor.go | 66 ++
backend/plugins/teambition/tasks/api_client.go | 48 +
.../plugins/teambition/tasks/project_collector.go | 85 ++
.../plugins/teambition/tasks/project_convertor.go | 80 ++
.../plugins/teambition/tasks/project_extractor.go | 60 ++
backend/plugins/teambition/tasks/shared.go | 172 ++++
.../plugins/teambition/tasks/sprint_collector.go | 84 ++
.../plugins/teambition/tasks/sprint_convertor.go | 87 ++
.../plugins/teambition/tasks/sprint_extractor.go | 60 ++
.../teambition/tasks/task_activity_collector.go | 104 ++
.../teambition/tasks/task_activity_extractor.go | 67 ++
.../teambition/tasks/task_changelog_converter.go | 81 ++
backend/plugins/teambition/tasks/task_collector.go | 84 ++
.../teambition/tasks/task_comment_converter.go | 86 ++
backend/plugins/teambition/tasks/task_converter.go | 181 ++++
backend/plugins/teambition/tasks/task_data.go | 66 ++
backend/plugins/teambition/tasks/task_extractor.go | 69 ++
.../teambition/tasks/task_flow_status_collector.go | 84 ++
.../teambition/tasks/task_flow_status_extractor.go | 61 ++
.../teambition/tasks/task_scenario_collector.go | 84 ++
.../teambition/tasks/task_scenario_extractor.go | 61 ++
.../plugins/teambition/tasks/task_tag_collector.go | 84 ++
.../plugins/teambition/tasks/task_tag_extractor.go | 67 ++
.../teambition/tasks/task_tag_task_converter.go | 80 ++
.../teambition/tasks/task_worktime_collector.go | 85 ++
.../teambition/tasks/task_worktime_converter.go | 82 ++
.../teambition/tasks/task_worktime_extractor.go | 67 ++
backend/plugins/teambition/teambition.go | 43 +
grafana/dashboards/Teambition.json | 1031 ++++++++++++++++++++
89 files changed, 5732 insertions(+)
diff --git a/backend/go.mod b/backend/go.mod
index 774212bb3..cdef1ec21 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -46,6 +46,7 @@ require (
)
require (
+ github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
diff --git a/backend/go.sum b/backend/go.sum
index 6ee6cae5b..b46e88bed 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -244,7 +244,10 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA=
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
+github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 h1:tDQ1LjKga657layZ4JLsRdxgvupebc0xuPwRNuTfUgs=
+github.com/golang-jwt/jwt/v5 v5.0.0-rc.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
diff --git a/backend/plugins/teambition/api/blueprint200.go b/backend/plugins/teambition/api/blueprint200.go
new file mode 100644
index 000000000..a4c6e044f
--- /dev/null
+++ b/backend/plugins/teambition/api/blueprint200.go
@@ -0,0 +1,107 @@
+/*
+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/teambition/models"
+ "time"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/core/utils"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+func MakeDataSourcePipelinePlanV200(subtaskMetas []plugin.SubTaskMeta, connectionId uint64, bpScopes []*plugin.BlueprintScopeV200, syncPolicy *plugin.BlueprintSyncPolicy) (plugin.PipelinePlan, []plugin.Scope, errors.Error) {
+ plan := make(plugin.PipelinePlan, len(bpScopes))
+ plan, err := makeDataSourcePipelinePlanV200(subtaskMetas, plan, bpScopes, connectionId, syncPolicy)
+ if err != nil {
+ return nil, nil, err
+ }
+ scopes, err := makeScopesV200(bpScopes, connectionId)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return plan, scopes, nil
+}
+
+func makeDataSourcePipelinePlanV200(
+ subtaskMetas []plugin.SubTaskMeta,
+ plan plugin.PipelinePlan,
+ bpScopes []*plugin.BlueprintScopeV200,
+ connectionId uint64,
+ syncPolicy *plugin.BlueprintSyncPolicy,
+) (plugin.PipelinePlan, errors.Error) {
+ for i, bpScope := range bpScopes {
+ stage := plan[i]
+ if stage == nil {
+ stage = plugin.PipelineStage{}
+ }
+ // construct task options for teambition
+ options := make(map[string]interface{})
+ options["projectId"] = bpScope.Id
+ options["connectionId"] = connectionId
+ if syncPolicy.TimeAfter != nil {
+ options["timeAfter"] = syncPolicy.TimeAfter.Format(time.RFC3339)
+ }
+
+ subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, bpScope.Entities)
+ if err != nil {
+ return nil, err
+ }
+ stage = append(stage, &plugin.PipelineTask{
+ Plugin: "teambition",
+ Subtasks: subtasks,
+ Options: options,
+ })
+ plan[i] = stage
+ }
+
+ return plan, nil
+}
+
+func makeScopesV200(bpScopes []*plugin.BlueprintScopeV200, connectionId uint64) ([]plugin.Scope, errors.Error) {
+ scopes := make([]plugin.Scope, 0)
+ for _, bpScope := range bpScopes {
+ project := &models.TeambitionProject{}
+ // get project from db
+ err := basicRes.GetDal().First(project,
+ dal.Where(`connection_id = ? and id = ?`,
+ connectionId, bpScope.Id))
+ if err != nil {
+ return nil, errors.Default.Wrap(err, fmt.Sprintf("fail to find project %s", bpScope.Id))
+ }
+ // add board to scopes
+ if utils.StringsContains(bpScope.Entities, plugin.DOMAIN_TYPE_TICKET) {
+ domainBoard := &ticket.Board{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: didgen.NewDomainIdGenerator(&models.TeambitionProject{}).Generate(project.ConnectionId, project.Id),
+ },
+ Name: project.Name,
+ }
+ scopes = append(scopes, domainBoard)
+ }
+ }
+ return scopes, nil
+}
diff --git a/backend/plugins/teambition/api/connection.go b/backend/plugins/teambition/api/connection.go
new file mode 100644
index 000000000..c228c6b9d
--- /dev/null
+++ b/backend/plugins/teambition/api/connection.go
@@ -0,0 +1,163 @@
+/*
+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 (
+ "context"
+ "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/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "github.com/apache/incubator-devlake/server/api/shared"
+ "net/http"
+)
+
+type TeambitionTestConnResponse struct {
+ shared.ApiBody
+ Connection *models.TeambitionConn
+}
+
+// TestConnection @Summary test teambition connection
+// @Description Test teambition Connection
+// @Tags plugins/teambition
+// @Param body body models.TeambitionConn true "json body"
+// @Success 200 {object} TeambitionTestConnResponse "Success"
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/teambition/test [POST]
+func TestConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+ // process input
+ var connection models.TeambitionConn
+ err := api.Decode(input.Body, &connection, vld)
+ if err != nil {
+ return nil, err
+ }
+
+ // test connection
+ apiClient, err := api.NewApiClientFromConnection(context.TODO(), basicRes, &connection)
+ if err != nil {
+ return nil, err
+ }
+
+ res, err := apiClient.Get("/org/info?orgId="+connection.TenantId, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if res.StatusCode != http.StatusOK {
+ return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("unexpected status code: %d", res.StatusCode))
+ }
+ resBody := tasks.TeambitionComRes[any]{}
+ err = api.UnmarshalResponse(res, &resBody)
+ if err != nil {
+ return nil, err
+ }
+ if resBody.Code != http.StatusOK {
+ return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("unexpected status code: %d", res.StatusCode))
+ }
+
+ body := TeambitionTestConnResponse{}
+ body.Success = true
+ body.Message = "success"
+ body.Connection = &connection
+ // output
+ return &plugin.ApiResourceOutput{Body: body, Status: 200}, nil
+}
+
+// PostConnections @Summary create teambition connection
+// @Description Create teambition connection
+// @Tags plugins/teambition
+// @Param body body models.TeambitionConnection true "json body"
+// @Success 200 {object} models.TeambitionConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/teambition/connections [POST]
+func PostConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+ // update from request and save to database
+ connection := &models.TeambitionConnection{}
+ err := connectionHelper.Create(connection, input)
+ if err != nil {
+ return nil, err
+ }
+ return &plugin.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil
+}
+
+// PatchConnection @Summary patch teambition connection
+// @Description Patch teambition connection
+// @Tags plugins/teambition
+// @Param body body models.TeambitionConnection true "json body"
+// @Success 200 {object} models.TeambitionConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/teambition/connections/{connectionId} [PATCH]
+func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+ connection := &models.TeambitionConnection{}
+ err := connectionHelper.Patch(connection, input)
+ if err != nil {
+ return nil, err
+ }
+ return &plugin.ApiResourceOutput{Body: connection}, nil
+}
+
+// DeleteConnection @Summary delete a teambition connection
+// @Description Delete a teambition connection
+// @Tags plugins/teambition
+// @Success 200 {object} models.TeambitionConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/teambition/connections/{connectionId} [DELETE]
+func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+ connection := &models.TeambitionConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ if err != nil {
+ return nil, err
+ }
+ err = connectionHelper.Delete(connection)
+ return &plugin.ApiResourceOutput{Body: connection}, err
+}
+
+// ListConnections @Summary get all teambition connections
+// @Description Get all teambition connections
+// @Tags plugins/teambition
+// @Success 200 {object} []models.TeambitionConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/teambition/connections [GET]
+func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+ var connections []models.TeambitionConnection
+ err := connectionHelper.List(&connections)
+ if err != nil {
+ return nil, err
+ }
+ return &plugin.ApiResourceOutput{Body: connections, Status: http.StatusOK}, nil
+}
+
+// GetConnection @Summary get teambition connection detail
+// @Description Get teambition connection detail
+// @Tags plugins/teambition
+// @Success 200 {object} models.TeambitionConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/teambition/connections/{connectionId} [GET]
+func GetConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+ connection := &models.TeambitionConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ return &plugin.ApiResourceOutput{Body: connection}, err
+}
diff --git a/backend/plugins/teambition/api/init.go b/backend/plugins/teambition/api/init.go
new file mode 100644
index 000000000..cd019a36f
--- /dev/null
+++ b/backend/plugins/teambition/api/init.go
@@ -0,0 +1,37 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/context"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/go-playground/validator/v10"
+)
+
+var vld *validator.Validate
+var connectionHelper *helper.ConnectionApiHelper
+var basicRes context.BasicRes
+
+func Init(br context.BasicRes) {
+ basicRes = br
+ vld = validator.New()
+ connectionHelper = helper.NewConnectionHelper(
+ basicRes,
+ vld,
+ )
+}
diff --git a/backend/plugins/teambition/e2e/changelogs_test.go b/backend/plugins/teambition/e2e/changelogs_test.go
new file mode 100644
index 000000000..b5acc1ebb
--- /dev/null
+++ b/backend/plugins/teambition/e2e/changelogs_test.go
@@ -0,0 +1,69 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionChangelogs(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_task_activities.csv",
+ "_raw_teambition_api_task_activities")
+ dataflowTester.FlushTabler(&models.TeambitionTaskActivity{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTaskActivitiesMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTaskActivity{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_task_activities.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{"created", "updated", "start_date", "end_date", "create_time", "update_time"},
+ },
+ )
+
+ dataflowTester.FlushTabler(&ticket.IssueChangelogs{})
+ dataflowTester.Subtask(tasks.ConvertTaskChangelogMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.IssueChangelogs{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_changelogs.csv",
+ IgnoreFields: []string{"created_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/projects_test.go b/backend/plugins/teambition/e2e/projects_test.go
new file mode 100644
index 000000000..b512c2df8
--- /dev/null
+++ b/backend/plugins/teambition/e2e/projects_test.go
@@ -0,0 +1,69 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionProject(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_projects.csv",
+ "_raw_teambition_api_projects")
+ dataflowTester.FlushTabler(&models.TeambitionProject{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractProjectsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionProject{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_projects.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{"created", "updated", "start_date", "end_date"},
+ },
+ )
+
+ dataflowTester.FlushTabler(&ticket.Board{})
+ dataflowTester.Subtask(tasks.ConvertProjectsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.Board{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/boards.csv",
+ IgnoreFields: []string{"created_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_projects.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_projects.csv
new file mode 100644
index 000000000..50955cf2a
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_projects.csv
@@ -0,0 +1,2 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-16T14:49:56.415Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[],""description"":"""",""endDate"":null,""id"":""64132c94f0d59df1c9825ab8"",""isArchived"":false,""isSuspended"":false,""isTemplate"":false,""logo"":""https://tcs-ga.teambition.net/thumbnail/312qf26a0a3a77f700a4fded1f2a4c19b257/w/600/h/300"",""name"":""缺陷管理"",""organizationId"":""640b1c30c933fd8 [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_sprints.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_sprints.csv
new file mode 100644
index 000000000..6c0756df0
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_sprints.csv
@@ -0,0 +1,4 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplished"":null,""created"":""2023-03-20T16:28:36.469Z"",""creatorId"":""5f27709685e4266322e2690a"",""description"":"""",""dueDate"":null,""id"":""641889b4547467946c9ad2c8"",""labels"":[],""name"":""beta1.0"",""projectId"":""64132c94f0d59df1c9825ab8"",""startDate"":null,""status"":""future"",""updated"":""2023-03-20T16:28:36.470Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59 [...]
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplished"":null,""created"":""2023-03-21T12:33:02.486Z"",""creatorId"":""5f27709685e4266322e2690a"",""description"":"""",""dueDate"":null,""id"":""6419a3fe514a20109f89e557"",""labels"":[],""name"":""beta2.0"",""projectId"":""64132c94f0d59df1c9825ab8"",""startDate"":null,""status"":""future"",""updated"":""2023-03-21T12:33:02.486Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59 [...]
+3,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplished"":null,""created"":""2023-03-21T12:33:10.134Z"",""creatorId"":""5f27709685e4266322e2690a"",""description"":"""",""dueDate"":null,""id"":""6419a406fbb99df0501fef07"",""labels"":[],""name"":""beta3.0"",""projectId"":""64132c94f0d59df1c9825ab8"",""startDate"":null,""status"":""future"",""updated"":""2023-03-21T12:33:10.134Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59 [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_activities.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_activities.csv
new file mode 100644
index 000000000..60c95075b
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_activities.csv
@@ -0,0 +1,64 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""641889e2f98ea19169bab8dd"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""641889e2f98ea19169bab8dd\"",\""content\"":\""testt42rfawe\""}}"",""createTime"":""2023-03-20T16:29:22.229Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889e22bde1652d0e6a927"",""updateTime"":""2023-03-20T16:29:22.229Z""}",https://open.teambitio [...]
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""content\"":\""【示例】账号绑定失败\"",\""_id\"":\""64132c945f3fd80070965938\""}}"",""createTime"":""2023-03-16T14:49:56.756Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64132c94875ec8661dbad223"",""updateTime"":""2023-03-16T14:49:56.756Z""}",https://open.teambition. [...]
+3,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a3d0e6a450725f9b8205"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a3d0e6a450725f9b8205\"",\""content\"":\""test6\""}}"",""createTime"":""2023-03-21T12:32:17.009Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a3d1875ec8661de1bdf4"",""updateTime"":""2023-03-21T12:32:17.009Z""}",https://open.teambition.com/a [...]
+4,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""64188f3e7e30eb94d86f8792"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""64188f3e7e30eb94d86f8792\"",\""content\"":\""风险\""}}"",""createTime"":""2023-03-20T16:52:14.585Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64188f3e2bde1652d0e6ae48"",""updateTime"":""2023-03-20T16:52:14.585Z""}",https://open.teambition.com/api/ [...]
+5,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""64132c945f3fd80070965939"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""content\"":\""【示例】App 登录报错\"",\""_id\"":\""64132c945f3fd80070965939\""}}"",""createTime"":""2023-03-16T14:49:56.774Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64132c942bde1652d0ca4796"",""updateTime"":""2023-03-16T14:49:56.774Z""}",https://open.teambitio [...]
+6,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a35ff98ea19169bb4a83"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a35ff98ea19169bb4a83\"",\""content\"":\""test3\""}}"",""createTime"":""2023-03-21T12:30:23.042Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a35f2bde1652d0f085e5"",""updateTime"":""2023-03-21T12:30:23.042Z""}",https://open.teambition.com/a [...]
+7,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a2df90097a8c84c5b7b8"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a2df90097a8c84c5b7b8\"",\""content\"":\""test1\""}}"",""createTime"":""2023-03-21T12:28:15.923Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a2df2bde1652d0f083d2"",""updateTime"":""2023-03-21T12:28:15.923Z""}",https://open.teambition.com/a [...]
+8,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a3c24bccff5385d90268"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a3c24bccff5385d90268\"",\""content\"":\""test4\""}}"",""createTime"":""2023-03-21T12:32:02.925Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a3c2875ec8661de1bdc2"",""updateTime"":""2023-03-21T12:32:02.925Z""}",https://open.teambition.com/a [...]
+9,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b1dabf79590a54dd3d75"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419b1dabf79590a54dd3d75\"",\""content\"":\""fasdzvaerrw\""}}"",""createTime"":""2023-03-21T13:32:10.308Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1da875ec8661de1f042"",""updateTime"":""2023-03-21T13:32:10.308Z""}",https://open.teambition [...]
+10,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b1c1640380c7aecefe0e"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419b1c1640380c7aecefe0e\"",\""content\"":\""fasdf\""}}"",""createTime"":""2023-03-21T13:31:45.093Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1c1875ec8661de1effa"",""updateTime"":""2023-03-21T13:31:45.093Z""}",https://open.teambition.com/ [...]
+11,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419aee0762f31f9b2168ca3"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419aee0762f31f9b2168ca3\"",\""content\"":\""bug1\""}}"",""createTime"":""2023-03-21T13:19:28.323Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419aee0875ec8661de1e7a5"",""updateTime"":""2023-03-21T13:19:28.323Z""}",https://open.teambition.com/a [...]
+12,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b17472707d4d15e64f86"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419b17472707d4d15e64f86\"",\""content\"":\""bug6\""}}"",""createTime"":""2023-03-21T13:30:28.852Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1742bde1652d0f0b78b"",""updateTime"":""2023-03-21T13:30:28.852Z""}",https://open.teambition.com/a [...]
+13,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a3e15f3fd8007098bd03"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a3e15f3fd8007098bd03\"",\""content\"":\""test7\""}}"",""createTime"":""2023-03-21T12:32:33.571Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a3e12bde1652d0f0879c"",""updateTime"":""2023-03-21T12:32:33.571Z""}",https://open.teambition.com/ [...]
+14,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b1654bccff5385d90590"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""content\"":\""bug4\"",\""_id\"":\""6419b1654bccff5385d90590\""}}"",""createTime"":""2023-03-21T13:30:13.335Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b165875ec8661de1eee8"",""updateTime"":""2023-03-21T13:30:13.335Z""}",https://open.teambition.com/a [...]
+15,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a466f407a6bb9c9e31ae"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a466f407a6bb9c9e31ae\"",\""content\"":\""test7\""}}"",""createTime"":""2023-03-21T12:34:46.202Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a466875ec8661de1bfc4"",""updateTime"":""2023-03-21T12:34:46.202Z""}",https://open.teambition.com/ [...]
+16,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419aee421643c55d9d1117f"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""content\"":\""bug2\"",\""_id\"":\""6419aee421643c55d9d1117f\""}}"",""createTime"":""2023-03-21T13:19:32.860Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419aee4875ec8661de1e7b0"",""updateTime"":""2023-03-21T13:19:32.860Z""}",https://open.teambition.com/a [...]
+17,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a2f9344ff5c7682abcc8"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a2f9344ff5c7682abcc8\"",\""content\"":\""fsdfdf\""}}"",""createTime"":""2023-03-21T12:28:41.443Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a2f9875ec8661de1bacd"",""updateTime"":""2023-03-21T12:28:41.443Z""}",https://open.teambition.com [...]
+18,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b1c8090e699c15cb72ee"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419b1c8090e699c15cb72ee\"",\""content\"":\""fasdfasd\""}}"",""createTime"":""2023-03-21T13:31:52.612Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1c82bde1652d0f0b861"",""updateTime"":""2023-03-21T13:31:52.612Z""}",https://open.teambition.c [...]
+19,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b1b54ed7d8c44b411ba6"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419b1b54ed7d8c44b411ba6\"",\""content\"":\""xuqiu1\""}}"",""createTime"":""2023-03-21T13:31:33.699Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1b5875ec8661de1efe3"",""updateTime"":""2023-03-21T13:31:33.699Z""}",https://open.teambition.com [...]
+20,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419aeeb1502a928dbcdb66e"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""content\"":\""bug3\"",\""_id\"":\""6419aeeb1502a928dbcdb66e\""}}"",""createTime"":""2023-03-21T13:19:39.863Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419aeeb2bde1652d0f0b0fb"",""updateTime"":""2023-03-21T13:19:39.863Z""}",https://open.teambition.com/a [...]
+21,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419a357bf79590a54dd3a28"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419a357bf79590a54dd3a28\"",\""content\"":\""test2\""}}"",""createTime"":""2023-03-21T12:30:15.726Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a3572bde1652d0f085c5"",""updateTime"":""2023-03-21T12:30:15.726Z""}",https://open.teambition.com/ [...]
+22,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""create"",""boundToObjectId"":""6419b16f7a4d42ee8e9246db"",""boundToObjectType"":""task"",""content"":""{\""task\"":{\""_id\"":\""6419b16f7a4d42ee8e9246db\"",\""content\"":\""bug5\""}}"",""createTime"":""2023-03-21T13:30:23.404Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b16f875ec8661de1ef1f"",""updateTime"":""2023-03-21T13:30:23.404Z""}",https://open.teambition.com/a [...]
+23,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""clear.customfield"",""boundToObjectId"":""641889e2f98ea19169bab8dd"",""boundToObjectType"":""task"",""content"":""{\""type\"":\""commongroup\"",\""name\"":\""需求分类\"",\""_customfieldId\"":\""6418896b70a2e66184e84629\"",\""value\"":[],\""values\"":[]}"",""createTime"":""2023-03-20T16:49:41.021Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64188ea5875ec8661dd7b0f7"",""updateT [...]
+24,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""add.tag"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""tag\"":\""bug\""}"",""createTime"":""2023-03-18T03:09:09.045Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64152b552bde1652d0d590b0"",""updateTime"":""2023-03-18T03:09:09.045Z""}",https://open.teambition.com/api/v3/task/64132c945f3fd80070965938/activity/list?pageSize [...]
+25,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.sprint"",""boundToObjectId"":""6419a3d0e6a450725f9b8205"",""boundToObjectType"":""task"",""content"":""{\""sprint\"":{\""_id\"":\""641889b4547467946c9ad2c8\"",\""name\"":\""beta1.0\""}}"",""createTime"":""2023-03-21T12:32:49.456Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a3f12bde1652d0f087ec"",""updateTime"":""2023-03-21T12:32:49.456Z""}",https://open.teambit [...]
+26,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.sprint"",""boundToObjectId"":""64188f3e7e30eb94d86f8792"",""boundToObjectType"":""task"",""content"":""{\""sprint\"":{\""_id\"":\""6419a406fbb99df0501fef07\"",\""name\"":\""beta3.0\""}}"",""createTime"":""2023-03-21T12:34:07.063Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a43f2bde1652d0f088bc"",""updateTime"":""2023-03-21T12:34:07.063Z""}",https://open.teambit [...]
+27,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""comment"",""boundToObjectId"":""64132c945f3fd80070965939"",""boundToObjectType"":""task"",""content"":""{\""isOnlyNotifyMentions\"":false,\""isDingtalkPM\"":true,\""renderMode\"":\""text\"",\""attachments\"":[],\""dingFiles\"":[],\""comment\"":\""test\""}"",""createTime"":""2023-03-18T03:54:22.963Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641535ee2bde1652d0d5d56a"",""u [...]
+28,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.sprint"",""boundToObjectId"":""6419a35ff98ea19169bb4a83"",""boundToObjectType"":""task"",""content"":""{\""sprint\"":{\""_id\"":\""6419a3fe514a20109f89e557\"",\""name\"":\""beta2.0\""}}"",""createTime"":""2023-03-21T12:33:25.128Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a4152bde1652d0f08832"",""updateTime"":""2023-03-21T12:33:25.128Z""}",https://open.teambit [...]
+29,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.sprint"",""boundToObjectId"":""6419a3c24bccff5385d90268"",""boundToObjectType"":""task"",""content"":""{\""sprint\"":{\""_id\"":\""6419a3fe514a20109f89e557\"",\""name\"":\""beta2.0\""}}"",""createTime"":""2023-03-21T12:33:25.312Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a4152bde1652d0f08833"",""updateTime"":""2023-03-21T12:33:25.312Z""}",https://open.teambit [...]
+30,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419b1dabf79590a54dd3d75"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""测试中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:32:24.654Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1e82bde1652d0f0b8bf"",""updateTime"":""2023-03-21T13:32 [...]
+31,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419b1c1640380c7aecefe0e"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""开发中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:32:31.063Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1ef875ec8661de1f070"",""updateTime"":""2023-03-21T13:32 [...]
+32,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419aee0762f31f9b2168ca3"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""修复中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:30:36.102Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b17c875ec8661de1ef45"",""updateTime"":""2023-03-21T13:30 [...]
+33,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419b17472707d4d15e64f86"",""boundToObjectType"":""task"",""content"":""{\""taskflowstatus\"":\""修复中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2,\""isDone\"":false}"",""createTime"":""2023-03-21T13:31:02.025Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b196875ec8661de1ef8d"",""updateTime"":""2023-03-21T13:31 [...]
+34,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419aee421643c55d9d1117f"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""已解决\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:30:39.966Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b17f2bde1652d0f0b7a4"",""updateTime"":""2023-03-21T13:30 [...]
+35,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419b1c8090e699c15cb72ee"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""测试中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:32:34.026Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1f2875ec8661de1f077"",""updateTime"":""2023-03-21T13:32 [...]
+36,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419b1b54ed7d8c44b411ba6"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""已完成\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:32:19.225Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1e32bde1652d0f0b8aa"",""updateTime"":""2023-03-21T13:32 [...]
+37,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419aeeb1502a928dbcdb66e"",""boundToObjectType"":""task"",""content"":""{\""taskflowstatus\"":\""已拒绝\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2,\""isDone\"":false}"",""createTime"":""2023-03-21T13:30:43.055Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b183875ec8661de1ef58"",""updateTime"":""2023-03-21T13:30 [...]
+38,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.sprint"",""boundToObjectId"":""6419a357bf79590a54dd3a28"",""boundToObjectType"":""task"",""content"":""{\""sprint\"":{\""_id\"":\""6419a406fbb99df0501fef07\"",\""name\"":\""beta3.0\""}}"",""createTime"":""2023-03-21T12:34:07.066Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a43f875ec8661de1bf3f"",""updateTime"":""2023-03-21T12:34:07.066Z""}",https://open.teambit [...]
+39,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""641889e2f98ea19169bab8dd"",""boundToObjectType"":""task"",""content"":""{\""actionVersion\"":2,\""isDone\"":false,\""taskflowstatus\"":\""开发中\"",\""oldTaskflowstatus\"":\""待处理\""}"",""createTime"":""2023-03-21T12:35:42.949Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a49e2bde1652d0f08a12"",""updateTime"":""2023-03-21T12:35 [...]
+40,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""comment"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""renderMode\"":\""text\"",\""attachments\"":[],\""dingFiles\"":[],\""comment\"":\""test\"",\""isOnlyNotifyMentions\"":false,\""isDingtalkPM\"":true}"",""createTime"":""2023-03-18T03:11:39.513Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64152beb2bde1652d0d59414"",""u [...]
+41,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.scenariofieldconfigId"",""boundToObjectId"":""6419a3d0e6a450725f9b8205"",""boundToObjectType"":""task"",""content"":""{\""newSfcName\"":\""需求\"",\""oldSfcName\"":\""任务\""}"",""createTime"":""2023-03-21T12:35:20.485Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a488875ec8661de1c026"",""updateTime"":""2023-03-21T12:35:20.485Z""}",https://open.teambition.com/api/v3 [...]
+42,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""add.tag"",""boundToObjectId"":""64132c945f3fd80070965939"",""boundToObjectType"":""task"",""content"":""{\""tag\"":\""story\""}"",""createTime"":""2023-03-18T08:47:55.965Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64157abb875ec8661dc7e2b0"",""updateTime"":""2023-03-18T08:47:55.965Z""}",https://open.teambition.com/api/v3/task/64132c945f3fd80070965939/activity/list?pageSi [...]
+43,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_startdate"",""boundToObjectId"":""6419a35ff98ea19169bb4a83"",""boundToObjectType"":""task"",""content"":""{\""startDate\"":\""2023-03-01T01:00:00.000Z\"",\""oldStartDate\"":null,\""actionVersion\"":2}"",""createTime"":""2023-03-21T12:33:37.209Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a4212bde1652d0f08860"",""updateTime"":""2023-03-21T12:33:37.209Z""}",https [...]
+44,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419b1c1640380c7aecefe0e"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""已完成\"",\""oldTaskflowstatus\"":\""开发中\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T13:33:06.678Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b2122bde1652d0f0b919"",""updateTime"":""2023-03-21T13:33 [...]
+45,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419a357bf79590a54dd3a28"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""工作中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T12:34:31.158Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a457875ec8661de1bf8f"",""updateTime"":""2023-03-21T12:34 [...]
+46,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_content"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""content\"":\""【示例】账号绑定失败11\"",\""oldContent\"":\""【示例】账号绑定失败\""}"",""createTime"":""2023-03-18T04:14:35.700Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64153aab875ec8661dc699d2"",""updateTime"":""2023-03-18T04:14:35.700Z""}",https://open.teambition.com/api/v [...]
+47,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419a3d0e6a450725f9b8205"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""待处理\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T12:35:20.487Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a4882bde1652d0f089a7"",""updateTime"":""2023-03-21T12:35 [...]
+48,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""64132c945f3fd80070965939"",""boundToObjectType"":""task"",""content"":""{\""taskflowstatus\"":\""工作中\"",\""oldTaskflowstatus\"":\""修复中\"",\""actionVersion\"":2,\""isDone\"":false}"",""createTime"":""2023-03-19T13:41:01.695Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641710ed875ec8661dcb13f2"",""updateTime"":""2023-03-19T13:41 [...]
+49,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_duedate"",""boundToObjectId"":""6419a35ff98ea19169bb4a83"",""boundToObjectType"":""task"",""content"":""{\""oldDueDate\"":null,\""actionVersion\"":2,\""dueDate\"":\""2023-03-31T10:00:00.000Z\""}"",""createTime"":""2023-03-21T12:33:42.511Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a426875ec8661de1bee7"",""updateTime"":""2023-03-21T12:33:42.511Z""}",https://ope [...]
+50,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""comment"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""comment\"":\""twst2\"",\""isOnlyNotifyMentions\"":false,\""isDingtalkPM\"":true,\""renderMode\"":\""text\"",\""attachments\"":[],\""dingFiles\"":[]}"",""createTime"":""2023-03-19T02:32:13.287Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6416742d875ec8661dc97f0c"","" [...]
+51,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419a3d0e6a450725f9b8205"",""boundToObjectType"":""task"",""content"":""{\""taskflowstatus\"":\""开发中\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2,\""isDone\"":false}"",""createTime"":""2023-03-21T13:32:16.576Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419b1e02bde1652d0f0b8a7"",""updateTime"":""2023-03-21T13:32 [...]
+52,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.sprint"",""boundToObjectId"":""64132c945f3fd80070965939"",""boundToObjectType"":""task"",""content"":""{\""sprint\"":{\""_id\"":\""641889b4547467946c9ad2c8\"",\""name\"":\""beta1.0\""}}"",""createTime"":""2023-03-20T16:30:04.507Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64188a0c875ec8661dd7ac7c"",""updateTime"":""2023-03-20T16:30:04.507Z""}",https://open.teambit [...]
+53,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""6419a35ff98ea19169bb4a83"",""boundToObjectType"":""task"",""content"":""{\""isDone\"":false,\""taskflowstatus\"":\""已解决\"",\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2}"",""createTime"":""2023-03-21T12:33:47.265Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a42b2bde1652d0f08884"",""updateTime"":""2023-03-21T12:33 [...]
+54,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""comment"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""isOnlyNotifyMentions\"":false,\""isDingtalkPM\"":true,\""renderMode\"":\""text\"",\""attachments\"":[],\""dingFiles\"":[],\""comment\"":\""423534\""}"",""createTime"":""2023-03-19T02:44:52.763Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64167724875ec8661dc98729""," [...]
+55,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""add.tag"",""boundToObjectId"":""6419a35ff98ea19169bb4a83"",""boundToObjectType"":""task"",""content"":""{\""tag\"":\""标签2\""}"",""createTime"":""2023-03-21T12:33:51.569Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a42f875ec8661de1bf17"",""updateTime"":""2023-03-21T12:33:51.569Z""}",https://open.teambition.com/api/v3/task/6419a35ff98ea19169bb4a83/activity/list?pageSize [...]
+56,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""standard"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""title\"":\""提交了 3月19日 计划工时 14 小时\"",\""icon\"":\""stopwatch\""}"",""createTime"":""2023-03-19T02:52:46.354Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641678fefadbca6b74267a64"",""updateTime"":""2023-03-19T02:52:46.354Z""}",https://open.teambition.com/api/v3/task/ [...]
+57,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""standard"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""title\"":\""提交了 3月19日 实际工时 10 小时\"",\""subtitle\"":\""\"",\""icon\"":\""stopwatch\""}"",""createTime"":""2023-03-19T02:52:54.707Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64167906c40b4a3162d31e50"",""updateTime"":""2023-03-19T02:52:54.707Z""}",https://open.teamb [...]
+58,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update.taskflowstatus"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""oldTaskflowstatus\"":\""待处理\"",\""actionVersion\"":2,\""isDone\"":false,\""taskflowstatus\"":\""已解决\""}"",""createTime"":""2023-03-19T05:20:02.546Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64169b822bde1652d0d91985"",""updateTime"":""2023-03-19T05:20 [...]
+59,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""standard"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""title\"":\""提交了 3月19日 实际工时 1.5 小时\"",\""subtitle\"":\""\"",\""icon\"":\""stopwatch\""}"",""createTime"":""2023-03-19T05:37:07.262Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64169f835538aa396dc8d13f"",""updateTime"":""2023-03-19T05:37:07.263Z""}",https://open.team [...]
+60,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_startdate"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""startDate\"":\""2023-03-20T01:00:00.000Z\"",\""oldStartDate\"":null,\""actionVersion\"":2}"",""createTime"":""2023-03-19T16:06:17.103Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641732f9875ec8661dcb8c4f"",""updateTime"":""2023-03-19T16:06:17.103Z""}",https [...]
+61,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_duedate"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""oldDueDate\"":null,\""actionVersion\"":2,\""dueDate\"":\""2023-03-23T10:00:00.000Z\""}"",""createTime"":""2023-03-19T16:06:21.293Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641732fd2bde1652d0dac377"",""updateTime"":""2023-03-19T16:06:21.293Z""}",https://ope [...]
+62,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_executor"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""_executorId\"":\""5f27709685e4266322e2690a\"",\""_oldExecutorId\"":null,\""actionVersion\"":2}"",""createTime"":""2023-03-19T16:53:30.157Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64173e0a2bde1652d0dacf00"",""updateTime"":""2023-03-19T16:53:30.157Z""}",ht [...]
+63,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""action"":""update_startdate"",""boundToObjectId"":""64132c945f3fd80070965938"",""boundToObjectType"":""task"",""content"":""{\""actionVersion\"":2,\""startDate\"":\""2023-03-17T01:00:00.000Z\"",\""oldStartDate\"":\""2023-03-20T01:00:00.000Z\""}"",""createTime"":""2023-03-19T17:31:53.371Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641747092bde1652d0dadeb7"",""updateTime"":""2023-03- [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_flow_status.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_flow_status.csv
new file mode 100644
index 000000000..469fc6df0
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_flow_status.csv
@@ -0,0 +1,31 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-16T14:49:56.463Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64132c9461bb57f8d78a13a1"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待处理"",""pos"":65536,""rejectStatusIds"":[""64132c9461bb57f8d78a13a4"",""64132c9461bb57f8d78a13a3"",""64132c9461bb57f8d78a13a2""],""taskflowId"":""64132c9461bb57f8d78a1396"",""updated"":""202 [...]
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:21.337Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889697ceb3c43de925302"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待处理"",""pos"":65536,""rejectStatusIds"":[""641889697ceb3c43de925306"",""641889697ceb3c43de925308"",""641889697ceb3c43de925304"",""641889697ceb3c43de92530a"",""641889697ceb3c43de92530c""],""t [...]
+3,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:23.221Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896b14f802bb89ad0e08"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待处理"",""pos"":65536,""rejectStatusIds"":[],""taskflowId"":""6418896b14f802bb89ad0e00"",""updated"":""2023-03-20T16:27:23.221Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+4,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:26.966Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896e7bd38f77c2991be6"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待评审"",""pos"":65536,""rejectStatusIds"":[],""taskflowId"":""6418896e7bd38f77c2991be4"",""updated"":""2023-03-20T16:27:26.966Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+5,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:31.967Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889731ea118756f2a51be"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待处理"",""pos"":65536,""rejectStatusIds"":[],""taskflowId"":""641889731ea118756f2a51bc"",""updated"":""2023-03-20T16:27:31.967Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+6,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:33.778Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889759d2485e0cca44ab2"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待处理"",""pos"":65536,""rejectStatusIds"":[],""taskflowId"":""641889759d2485e0cca44ab0"",""updated"":""2023-03-20T16:27:33.778Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+7,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:53:55.744Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64188fa389e95908d4c3bfca"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""待处理"",""pos"":65536,""rejectStatusIds"":[],""taskflowId"":""64188fa389e95908d4c3bfc3"",""updated"":""2023-03-20T16:53:55.744Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+8,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-16T14:49:56.464Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64132c9461bb57f8d78a13a3"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已解决"",""pos"":98304,""rejectStatusIds"":[""64132c9461bb57f8d78a13a2""],""taskflowId"":""64132c9461bb57f8d78a1396"",""updated"":""2023-03-16T14:49:56.783Z""}",https://open.teambition.com/api/v3 [...]
+9,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:21.341Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889697ceb3c43de925306"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""重新打开"",""pos"":98304,""rejectStatusIds"":[""641889697ceb3c43de925302"",""641889697ceb3c43de925308""],""taskflowId"":""641889697ceb3c43de925300"",""updated"":""2023-03-20T16:27:21.351Z""}",ht [...]
+10,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-16T14:49:56.464Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64132c9461bb57f8d78a13a4"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""修复中"",""pos"":131072,""rejectStatusIds"":[""64132c9461bb57f8d78a13a3"",""64132c9461bb57f8d78a13a2""],""taskflowId"":""64132c9461bb57f8d78a1396"",""updated"":""2023-03-21T12:28:29.096Z""}",h [...]
+11,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:21.343Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889697ceb3c43de925308"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""修复中"",""pos"":131072,""rejectStatusIds"":[""641889697ceb3c43de92530a"",""641889697ceb3c43de92530c"",""641889697ceb3c43de925304""],""taskflowId"":""641889697ceb3c43de925300"",""updated"":""2 [...]
+12,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:23.215Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896b14f802bb89ad0e04"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""开发中"",""pos"":131072,""rejectStatusIds"":[],""taskflowId"":""6418896b14f802bb89ad0e00"",""updated"":""2023-03-20T16:27:23.215Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+13,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:26.971Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896e7bd38f77c2991be9"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""未执行"",""pos"":131072,""rejectStatusIds"":[],""taskflowId"":""6418896e7bd38f77c2991be4"",""updated"":""2023-03-20T16:27:26.971Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+14,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:31.974Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889731ea118756f2a51c4"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已完成"",""pos"":131072,""rejectStatusIds"":[],""taskflowId"":""641889731ea118756f2a51bc"",""updated"":""2023-03-20T16:27:31.974Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+15,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:33.780Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889759d2485e0cca44ab4"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""进行中"",""pos"":131072,""rejectStatusIds"":[],""taskflowId"":""641889759d2485e0cca44ab0"",""updated"":""2023-03-20T16:27:33.780Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+16,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:53:55.764Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64188fa389e95908d4c3bfdd"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""进行中"",""pos"":131072,""rejectStatusIds"":[],""taskflowId"":""64188fa389e95908d4c3bfc3"",""updated"":""2023-03-20T16:53:55.764Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+17,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:21.344Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889697ceb3c43de92530a"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已解决"",""pos"":196608,""rejectStatusIds"":[""641889697ceb3c43de925304"",""641889697ceb3c43de92530c""],""taskflowId"":""641889697ceb3c43de925300"",""updated"":""2023-03-20T16:27:21.354Z""}",htt [...]
+18,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:23.213Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896b14f802bb89ad0e02"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""测试中"",""pos"":196608,""rejectStatusIds"":[],""taskflowId"":""6418896b14f802bb89ad0e00"",""updated"":""2023-03-20T16:27:23.213Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+19,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:26.975Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896e7bd38f77c2991bec"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已通过"",""pos"":196608,""rejectStatusIds"":[],""taskflowId"":""6418896e7bd38f77c2991be4"",""updated"":""2023-03-20T16:27:26.975Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+20,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:33.781Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889759d2485e0cca44ab6"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已完成"",""pos"":196608,""rejectStatusIds"":[],""taskflowId"":""641889759d2485e0cca44ab0"",""updated"":""2023-03-20T16:27:33.781Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+21,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:53:55.772Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64188fa389e95908d4c3bfe0"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已完成"",""pos"":196608,""rejectStatusIds"":[],""taskflowId"":""64188fa389e95908d4c3bfc3"",""updated"":""2023-03-20T16:53:55.772Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+22,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:21.339Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889697ceb3c43de925304"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已拒绝"",""pos"":262144,""rejectStatusIds"":[""641889697ceb3c43de92530a"",""641889697ceb3c43de92530c""],""taskflowId"":""641889697ceb3c43de925300"",""updated"":""2023-03-20T16:27:21.350Z""}",htt [...]
+23,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:23.218Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896b14f802bb89ad0e06"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已完成"",""pos"":262144,""rejectStatusIds"":[],""taskflowId"":""6418896b14f802bb89ad0e00"",""updated"":""2023-03-20T16:27:23.218Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+24,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:26.979Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896e7bd38f77c2991bef"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""阻塞"",""pos"":262144,""rejectStatusIds"":[],""taskflowId"":""6418896e7bd38f77c2991be4"",""updated"":""2023-03-20T16:27:26.979Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1 [...]
+25,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:33.783Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889759d2485e0cca44ab8"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""已取消"",""pos"":262144,""rejectStatusIds"":[],""taskflowId"":""641889759d2485e0cca44ab0"",""updated"":""2023-03-20T16:27:33.783Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c [...]
+26,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-21T05:22:49.451Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64193f2930f1deaea3ac9303"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""start"",""name"":""修复中"",""pos"":262144,""rejectStatusIds"":[],""taskflowId"":""64188fa389e95908d4c3bfc3"",""updated"":""2023-03-21T05:22:49.451Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+27,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-16T14:49:56.463Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64132c9461bb57f8d78a13a2"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""关闭"",""pos"":327680,""rejectStatusIds"":[],""taskflowId"":""64132c9461bb57f8d78a1396"",""updated"":""2023-03-16T14:49:56.765Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9 [...]
+28,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:21.346Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641889697ceb3c43de92530c"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""end"",""name"":""关闭"",""pos"":327680,""rejectStatusIds"":[],""taskflowId"":""641889697ceb3c43de925300"",""updated"":""2023-03-20T16:27:21.355Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9 [...]
+29,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-20T16:27:26.982Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6418896e7bd38f77c2991bf2"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""未通过"",""pos"":327680,""rejectStatusIds"":[],""taskflowId"":""6418896e7bd38f77c2991be4"",""updated"":""2023-03-20T16:27:26.982Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
+30,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""created"":""2023-03-19T13:40:51.972Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""641710e39039516d00cecc42"",""isDeleted"":false,""isTaskflowstatusruleexector"":false,""kind"":""unset"",""name"":""工作中"",""pos"":393216,""rejectStatusIds"":[],""taskflowId"":""64132c9461bb57f8d78a1396"",""updated"":""2023-03-19T13:40:52.116Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_scenarios.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_scenarios.csv
new file mode 100644
index 000000000..32f89f6cb
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_scenarios.csv
@@ -0,0 +1,7 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""boundToObjectId"":""64132c94f0d59df1c9825ab8"",""boundToObjectType"":""project"",""created"":""2019-01-07T07:58:46.787Z"",""creatorId"":""5f27709685e4266322e2690a"",""icon"":""task"",""id"":""64132c9461bb57f8d78a13b9"",""isArchived"":false,""name"":""任务"",""scenariofields"":[{""fieldType"":""sprint"",""required"":false},{""fieldType"":""note"",""required"":false},{""fieldType"":""priority"","" [...]
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""boundToObjectId"":""64132c94f0d59df1c9825ab8"",""boundToObjectType"":""project"",""created"":""2023-03-20T16:27:21.362Z"",""creatorId"":""5f27709685e4266322e2690a"",""icon"":""bug"",""id"":""641889697ceb3c43de925315"",""isArchived"":false,""name"":""缺陷"",""scenariofields"":[{""fieldType"":""sprint"",""required"":false},{""customfieldId"":null,""fieldType"":""note"",""required"":true},{""custom [...]
+3,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""boundToObjectId"":""64132c94f0d59df1c9825ab8"",""boundToObjectType"":""project"",""created"":""2023-03-20T16:27:23.230Z"",""creatorId"":""5f27709685e4266322e2690a"",""icon"":""requirement"",""id"":""6418896b14f802bb89ad0e0b"",""isArchived"":false,""name"":""需求"",""scenariofields"":[{""fieldType"":""sprint"",""required"":false},{""fieldType"":""note"",""required"":false},{""fieldType"":""storyP [...]
+4,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""boundToObjectId"":""64132c94f0d59df1c9825ab8"",""boundToObjectType"":""project"",""created"":""2023-03-20T16:27:31.954Z"",""creatorId"":""5f27709685e4266322e2690a"",""icon"":""milestone"",""id"":""641889731ea118756f2a51b6"",""isArchived"":false,""name"":""里程碑"",""scenariofields"":[{""fieldType"":""sprint"",""required"":false},{""customfieldId"":null,""fieldType"":""note"",""required"":false},{ [...]
+5,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""boundToObjectId"":""64132c94f0d59df1c9825ab8"",""boundToObjectType"":""project"",""created"":""2023-03-20T16:27:33.791Z"",""creatorId"":""5f27709685e4266322e2690a"",""icon"":""risk"",""id"":""641889759d2485e0cca44abc"",""isArchived"":false,""name"":""风险"",""scenariofields"":[{""fieldType"":""sprint"",""required"":false},{""fieldType"":""note"",""required"":false},{""customfieldId"":""64188975a [...]
+6,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""boundToObjectId"":""64132c94f0d59df1c9825ab8"",""boundToObjectType"":""project"",""created"":""2023-03-20T16:53:55.706Z"",""creatorId"":""5f27709685e4266322e2690a"",""icon"":""subtask"",""id"":""64188fa389e95908d4c3bfaf"",""isArchived"":false,""name"":""测试任务类型"",""scenariofields"":[{""fieldType"":""sprint"",""required"":false},{""fieldType"":""note"",""required"":false},{""fieldType"":""priori [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_tags.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_tags.csv
new file mode 100644
index 000000000..c801188a3
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_tags.csv
@@ -0,0 +1,8 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""blue"",""created"":""2023-03-18T03:09:08.727Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64152b54ed4ba1c701025878"",""isArchived"":null,""name"":""bug"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-18T03:09:08.723Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1,null,2023-03-23 14:24:40.646
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""cyan"",""created"":""2023-03-18T08:47:55.785Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""64157abbd068d1a90d907fd4"",""isArchived"":null,""name"":""story"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-18T08:47:55.783Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1&pageToken=1,null,2023- [...]
+3,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""green"",""created"":""2023-03-21T12:30:42.206Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a372a6216665fa0ecd16"",""isArchived"":null,""name"":""标签1"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-21T12:30:42.204Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1&pageToken=2,null,2023-0 [...]
+4,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""cyan"",""created"":""2023-03-21T12:30:48.274Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a37873f833cc51a976a0"",""isArchived"":null,""name"":""标签2"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-21T12:30:48.272Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1&pageToken=3,null,2023-03 [...]
+5,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""red"",""created"":""2023-03-21T12:30:53.950Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a37dc40b4a3162675583"",""isArchived"":null,""name"":""标签3"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-21T12:30:53.948Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1&pageToken=4,null,2023-03- [...]
+6,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""purple"",""created"":""2023-03-21T12:30:59.850Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a383282bbf185553fdfe"",""isArchived"":null,""name"":""标签5"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-21T12:30:59.848Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1&pageToken=5,null,2023- [...]
+7,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""color"":""red"",""created"":""2023-03-21T12:31:08.166Z"",""creatorId"":""5f27709685e4266322e2690a"",""id"":""6419a38c589562163cb29de7"",""isArchived"":null,""name"":""标签"",""projectId"":""64132c94f0d59df1c9825ab8"",""tagcategoryIds"":[],""updated"":""2023-03-21T12:31:08.165Z""}",https://open.teambition.com/api/v3/project/64132c94f0d59df1c9825ab8/tag/search?pageSize=1&pageToken=6,null,2023-03-2 [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_worktime.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_worktime.csv
new file mode 100644
index 000000000..5cab5cb97
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_task_worktime.csv
@@ -0,0 +1,3 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""createdAt"":""2023-03-19T02:52:54.645Z"",""date"":""2023-03-19T00:00:00.000Z"",""description"":"""",""objectId"":""64132c945f3fd80070965938"",""objectType"":""task"",""orgId"":""640b1c30c933fd85bb11ca31"",""submitterId"":""5f27709685e4266322e2690a"",""updatedAt"":""2023-03-19T02:52:54.645Z"",""userId"":""5f27709685e4266322e2690a"",""worktime"":36000000,""worktimeId"":""641679064214fd004249c7e8 [...]
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""createdAt"":""2023-03-19T05:37:07.197Z"",""date"":""2023-03-19T00:00:00.000Z"",""description"":"""",""objectId"":""64132c945f3fd80070965938"",""objectType"":""task"",""orgId"":""640b1c30c933fd85bb11ca31"",""submitterId"":""5f27709685e4266322e2690a"",""updatedAt"":""2023-03-19T05:37:07.197Z"",""userId"":""5f27709685e4266322e2690a"",""worktime"":5400000,""worktimeId"":""64169f83c75a9900466c9d25" [...]
diff --git a/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_tasks.csv b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_tasks.csv
new file mode 100644
index 000000000..195e60e3e
--- /dev/null
+++ b/backend/plugins/teambition/e2e/raw_tables/_raw_teambition_api_tasks.csv
@@ -0,0 +1,23 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":""2023-03-19T05:20:02.571Z"",""ancestorIds"":[],""content"":""【示例】账号绑定失败11"",""created"":""2023-03-16T14:49:56.617Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""5ec6001f852a6181e4028090"",""type"":""number"",""value"":[{""id"":""workTime"",""metaString"":""{\""unit\"":\""minute\""}"",""title"":""840""}]},{""cfId"":""5ec6028e8d0424e9b7508861"",""ty [...]
+2,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""【示例】App 登录报错"",""created"":""2023-03-16T14:49:56.618Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:plantime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2 [...]
+3,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""testt42rfawe"",""created"":""2023-03-20T16:29:22.116Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:plantime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2 [...]
+4,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""风险"",""created"":""2023-03-20T16:52:14.510Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:worktime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""value [...]
+5,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""test1"",""created"":""2023-03-21T12:28:15.811Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419a2df90097a8c84c5b7b8"",""involveMembers"":[""5f27709685e4266322e2690a""],""isArchived"":false,""isDone"":false,""note"":""sdfa"",""priority"":-10,""project [...]
+6,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""fsdfdf"",""created"":""2023-03-21T12:28:41.272Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419a2f9344ff5c7682abcc8"",""involveMembers"":[""5f27709685e4266322e2690a""],""isArchived"":false,""isDone"":false,""note"":""fasdf"",""priority"":-10,""proje [...]
+7,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""test2"",""created"":""2023-03-21T12:30:15.504Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:worktime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""va [...]
+8,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":""2023-03-21T12:33:47.290Z"",""ancestorIds"":[],""content"":""test3"",""created"":""2023-03-21T12:30:22.973Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:worktime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6a509e6c751111854""," [...]
+9,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""test4"",""created"":""2023-03-21T12:32:02.850Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419a3c24bccff5385d90268"",""involveMembers"":[""5f27709685e4266322e2690a""],""isArchived"":false,""isDone"":false,""note"":""fasdfafa"",""priority"":0,""proje [...]
+10,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""test6"",""created"":""2023-03-21T12:32:16.899Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:plantime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2"",""v [...]
+11,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""test7"",""created"":""2023-03-21T12:32:33.504Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:plantime"",""metaString"":""{\""unit\"":\""ms\""}"",""title"":""0""}]},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2"",""v [...]
+12,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""test7"",""created"":""2023-03-21T12:34:46.097Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419a466f407a6bb9c9e31ae"",""involveMembers"":[""5f27709685e4266322e2690a""],""isArchived"":false,""isDone"":false,""note"":""fasd"",""priority"":-10,""projec [...]
+13,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""bug1"",""created"":""2023-03-21T13:19:28.260Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419aee0762f31f9b2168ca3"",""involveMembers"":[""5f27709685e4266322e2690a""],""isA [...]
+14,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":""2023-03-21T13:30:40.008Z"",""ancestorIds"":[],""content"":""bug2"",""created"":""2023-03-21T13:19:32.761Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419aee421643c55d9d1117f"",""involveMembers"":[""5f2770968 [...]
+15,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":""2023-03-21T13:30:43.083Z"",""ancestorIds"":[],""content"":""bug3"",""created"":""2023-03-21T13:19:39.808Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419aeeb1502a928dbcdb66e"",""involveMembers"":[""5f2770968 [...]
+16,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""bug4"",""created"":""2023-03-21T13:30:13.211Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419b1654bccff5385d90590"",""involveMembers"":[""5f27709685e4266322e2690a""],""isA [...]
+17,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""bug5"",""created"":""2023-03-21T13:30:23.337Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419b16f7a4d42ee8e9246db"",""involveMembers"":[""5f27709685e4266322e2690a""],""isA [...]
+18,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""bug6"",""created"":""2023-03-21T13:30:28.787Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419b17472707d4d15e64f86"",""involveMembers"":[""5f27709685e4266322e2690a""],""isA [...]
+19,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":""2023-03-21T13:32:19.250Z"",""ancestorIds"":[],""content"":""xuqiu1"",""created"":""2023-03-21T13:31:33.629Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup"",""value"":[]},{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:plantime"",""metaString"":""{\" [...]
+20,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":""2023-03-21T13:33:06.709Z"",""ancestorIds"":[],""content"":""fasdf"",""created"":""2023-03-21T13:31:44.997Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419b1c1640380c7aecefe0e"",""involveMembers"":[""5f277096 [...]
+21,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""fasdfasd"",""created"":""2023-03-21T13:31:52.371Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup"",""value"":[]}],""dueDate"":null,""executorId"":""5f27709685e4266322e2690a"",""id"":""6419b1c8090e699c15cb72ee"",""involveMembers"":[""5f27709685e4266322e2690a""]," [...]
+22,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}","{""accomplishTime"":null,""ancestorIds"":[],""content"":""fasdzvaerrw"",""created"":""2023-03-21T13:32:10.205Z"",""creatorId"":""5f27709685e4266322e2690a"",""customfields"":[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup"",""value"":[]},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2"",""value"":[{""id"":""worktime:total:worktime"",""metaString"":""{\""unit\"":\""ms\""}" [...]
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_accounts.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_accounts.csv
new file mode 100644
index 000000000..37ee6e757
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_accounts.csv
@@ -0,0 +1,2 @@
+connection_id,user_id,member_id,is_disabled,role,avatar_url,birthday,city,province,country,email,entry_time,name,phone,title,pinyin,py,staff_type,employee_number,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,5f27709685e4266322e2690a,"",0,0,https://tcs.teambition.net/thumbnail/112042983b05e2f2cc22822e30beb7bdd668/w/100/h/100,"","","","","",,coldgust,"","","","","","",2023-03-23 14:24:36.273,2023-03-23 14:24:36.273,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_users,1,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_projects.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_projects.csv
new file mode 100644
index 000000000..69c565cf4
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_projects.csv
@@ -0,0 +1,2 @@
+connection_id,id,name,logo,description,organization_id,visibility,is_template,creator_id,is_archived,is_suspended,unique_id_prefix,created,updated,start_date,end_date,customfields,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c94f0d59df1c9825ab8,缺陷管理,https://tcs-ga.teambition.net/thumbnail/312qf26a0a3a77f700a4fded1f2a4c19b257/w/600/h/300,"",640b1c30c933fd85bb11ca31,project,0,5f27709685e4266322e2690a,0,0,"",2023-03-16 14:49:56.415,2023-03-16 14:49:56.415,,,[],2023-03-23 14:24:57.135,2023-03-23 14:24:57.135,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_projects,1,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_sprints.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_sprints.csv
new file mode 100644
index 000000000..d46a3d9e4
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_sprints.csv
@@ -0,0 +1,4 @@
+connection_id,id,name,executor_id,description,status,project_id,creator_id,start_date,due_date,accomplished,created,updated,payload,labels,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,641889b4547467946c9ad2c8,beta1.0,"","",future,64132c94f0d59df1c9825ab8,5f27709685e4266322e2690a,,,,2023-03-20 16:28:36.469,2023-03-20 16:28:36.470,,[],2023-03-23 14:24:57.676,2023-03-23 14:24:57.676,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,1,""
+1,6419a3fe514a20109f89e557,beta2.0,"","",future,64132c94f0d59df1c9825ab8,5f27709685e4266322e2690a,,,,2023-03-21 12:33:02.486,2023-03-21 12:33:02.486,,[],2023-03-23 14:24:57.676,2023-03-23 14:24:57.676,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,2,""
+1,6419a406fbb99df0501fef07,beta3.0,"","",future,64132c94f0d59df1c9825ab8,5f27709685e4266322e2690a,,,,2023-03-21 12:33:10.134,2023-03-21 12:33:10.134,,[],2023-03-23 14:24:57.676,2023-03-23 14:24:57.676,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,3,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_activities.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_activities.csv
new file mode 100644
index 000000000..1afbfe328
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_activities.csv
@@ -0,0 +1,64 @@
+connection_id,id,project_id,task_id,creator_id,action,bound_to_object_id,bound_to_object_type,create_time,update_time,content,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c942bde1652d0ca4796,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,5f27709685e4266322e2690a,create,64132c945f3fd80070965939,task,2023-03-16 14:49:56.774,2023-03-16 14:49:56.774,"{""task"":{""content"":""【示例】App 登录报错"",""_id"":""64132c945f3fd80070965939""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,5,""
+1,64132c94875ec8661dbad223,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,create,64132c945f3fd80070965938,task,2023-03-16 14:49:56.756,2023-03-16 14:49:56.756,"{""task"":{""content"":""【示例】账号绑定失败"",""_id"":""64132c945f3fd80070965938""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,2,""
+1,64152b552bde1652d0d590b0,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,add.tag,64132c945f3fd80070965938,task,2023-03-18 03:09:09.045,2023-03-18 03:09:09.045,"{""tag"":""bug""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,24,""
+1,64152beb2bde1652d0d59414,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,comment,64132c945f3fd80070965938,task,2023-03-18 03:11:39.513,2023-03-18 03:11:39.513,"{""renderMode"":""text"",""attachments"":[],""dingFiles"":[],""comment"":""test"",""isOnlyNotifyMentions"":false,""isDingtalkPM"":true}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_act [...]
+1,641535ee2bde1652d0d5d56a,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,5f27709685e4266322e2690a,comment,64132c945f3fd80070965939,task,2023-03-18 03:54:22.963,2023-03-18 03:54:22.963,"{""isOnlyNotifyMentions"":false,""isDingtalkPM"":true,""renderMode"":""text"",""attachments"":[],""dingFiles"":[],""comment"":""test""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_act [...]
+1,64153aab875ec8661dc699d2,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,update_content,64132c945f3fd80070965938,task,2023-03-18 04:14:35.700,2023-03-18 04:14:35.700,"{""content"":""【示例】账号绑定失败11"",""oldContent"":""【示例】账号绑定失败""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,46,""
+1,64157abb875ec8661dc7e2b0,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,5f27709685e4266322e2690a,add.tag,64132c945f3fd80070965939,task,2023-03-18 08:47:55.965,2023-03-18 08:47:55.965,"{""tag"":""story""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,42,""
+1,6416742d875ec8661dc97f0c,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,comment,64132c945f3fd80070965938,task,2023-03-19 02:32:13.287,2023-03-19 02:32:13.287,"{""comment"":""twst2"",""isOnlyNotifyMentions"":false,""isDingtalkPM"":true,""renderMode"":""text"",""attachments"":[],""dingFiles"":[]}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_ac [...]
+1,64167724875ec8661dc98729,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,comment,64132c945f3fd80070965938,task,2023-03-19 02:44:52.763,2023-03-19 02:44:52.763,"{""isOnlyNotifyMentions"":false,""isDingtalkPM"":true,""renderMode"":""text"",""attachments"":[],""dingFiles"":[],""comment"":""423534""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_a [...]
+1,641678fefadbca6b74267a64,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,standard,64132c945f3fd80070965938,task,2023-03-19 02:52:46.354,2023-03-19 02:52:46.354,"{""title"":""提交了 3月19日 计划工时 14 小时"",""icon"":""stopwatch""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,56,""
+1,64167906c40b4a3162d31e50,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,standard,64132c945f3fd80070965938,task,2023-03-19 02:52:54.707,2023-03-19 02:52:54.707,"{""title"":""提交了 3月19日 实际工时 10 小时"",""subtitle"":"""",""icon"":""stopwatch""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,57,""
+1,64169b822bde1652d0d91985,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,update.taskflowstatus,64132c945f3fd80070965938,task,2023-03-19 05:20:02.546,2023-03-19 05:20:02.546,"{""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false,""taskflowstatus"":""已解决""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,58,""
+1,64169f835538aa396dc8d13f,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,standard,64132c945f3fd80070965938,task,2023-03-19 05:37:07.262,2023-03-19 05:37:07.263,"{""title"":""提交了 3月19日 实际工时 1.5 小时"",""subtitle"":"""",""icon"":""stopwatch""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,59,""
+1,641710ed875ec8661dcb13f2,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,5f27709685e4266322e2690a,update.taskflowstatus,64132c945f3fd80070965939,task,2023-03-19 13:41:01.695,2023-03-19 13:41:01.695,"{""taskflowstatus"":""工作中"",""oldTaskflowstatus"":""修复中"",""actionVersion"":2,""isDone"":false}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,48,""
+1,641732f9875ec8661dcb8c4f,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,update_startdate,64132c945f3fd80070965938,task,2023-03-19 16:06:17.103,2023-03-19 16:06:17.103,"{""startDate"":""2023-03-20T01:00:00.000Z"",""oldStartDate"":null,""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,60,""
+1,641732fd2bde1652d0dac377,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,update_duedate,64132c945f3fd80070965938,task,2023-03-19 16:06:21.293,2023-03-19 16:06:21.293,"{""oldDueDate"":null,""actionVersion"":2,""dueDate"":""2023-03-23T10:00:00.000Z""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,61,""
+1,64173e0a2bde1652d0dacf00,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,update_executor,64132c945f3fd80070965938,task,2023-03-19 16:53:30.157,2023-03-19 16:53:30.157,"{""_executorId"":""5f27709685e4266322e2690a"",""_oldExecutorId"":null,""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,62,""
+1,641747092bde1652d0dadeb7,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,5f27709685e4266322e2690a,update_startdate,64132c945f3fd80070965938,task,2023-03-19 17:31:53.371,2023-03-19 17:31:53.371,"{""actionVersion"":2,""startDate"":""2023-03-17T01:00:00.000Z"",""oldStartDate"":""2023-03-20T01:00:00.000Z""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,63,""
+1,641889e22bde1652d0e6a927,64132c94f0d59df1c9825ab8,641889e2f98ea19169bab8dd,5f27709685e4266322e2690a,create,641889e2f98ea19169bab8dd,task,2023-03-20 16:29:22.229,2023-03-20 16:29:22.229,"{""task"":{""_id"":""641889e2f98ea19169bab8dd"",""content"":""testt42rfawe""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,1,""
+1,64188a0c875ec8661dd7ac7c,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,5f27709685e4266322e2690a,update.sprint,64132c945f3fd80070965939,task,2023-03-20 16:30:04.507,2023-03-20 16:30:04.507,"{""sprint"":{""_id"":""641889b4547467946c9ad2c8"",""name"":""beta1.0""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,52,""
+1,64188ea5875ec8661dd7b0f7,64132c94f0d59df1c9825ab8,641889e2f98ea19169bab8dd,5f27709685e4266322e2690a,clear.customfield,641889e2f98ea19169bab8dd,task,2023-03-20 16:49:41.021,2023-03-20 16:49:41.021,"{""type"":""commongroup"",""name"":""需求分类"",""_customfieldId"":""6418896b70a2e66184e84629"",""value"":[],""values"":[]}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,23,""
+1,64188f3e2bde1652d0e6ae48,64132c94f0d59df1c9825ab8,64188f3e7e30eb94d86f8792,5f27709685e4266322e2690a,create,64188f3e7e30eb94d86f8792,task,2023-03-20 16:52:14.585,2023-03-20 16:52:14.585,"{""task"":{""_id"":""64188f3e7e30eb94d86f8792"",""content"":""风险""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,4,""
+1,6419a2df2bde1652d0f083d2,64132c94f0d59df1c9825ab8,6419a2df90097a8c84c5b7b8,5f27709685e4266322e2690a,create,6419a2df90097a8c84c5b7b8,task,2023-03-21 12:28:15.923,2023-03-21 12:28:15.923,"{""task"":{""_id"":""6419a2df90097a8c84c5b7b8"",""content"":""test1""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,7,""
+1,6419a2f9875ec8661de1bacd,64132c94f0d59df1c9825ab8,6419a2f9344ff5c7682abcc8,5f27709685e4266322e2690a,create,6419a2f9344ff5c7682abcc8,task,2023-03-21 12:28:41.443,2023-03-21 12:28:41.443,"{""task"":{""_id"":""6419a2f9344ff5c7682abcc8"",""content"":""fsdfdf""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,17,""
+1,6419a3572bde1652d0f085c5,64132c94f0d59df1c9825ab8,6419a357bf79590a54dd3a28,5f27709685e4266322e2690a,create,6419a357bf79590a54dd3a28,task,2023-03-21 12:30:15.726,2023-03-21 12:30:15.726,"{""task"":{""_id"":""6419a357bf79590a54dd3a28"",""content"":""test2""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,21,""
+1,6419a35f2bde1652d0f085e5,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,5f27709685e4266322e2690a,create,6419a35ff98ea19169bb4a83,task,2023-03-21 12:30:23.042,2023-03-21 12:30:23.042,"{""task"":{""_id"":""6419a35ff98ea19169bb4a83"",""content"":""test3""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,6,""
+1,6419a3c2875ec8661de1bdc2,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,5f27709685e4266322e2690a,create,6419a3c24bccff5385d90268,task,2023-03-21 12:32:02.925,2023-03-21 12:32:02.925,"{""task"":{""_id"":""6419a3c24bccff5385d90268"",""content"":""test4""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,8,""
+1,6419a3d1875ec8661de1bdf4,64132c94f0d59df1c9825ab8,6419a3d0e6a450725f9b8205,5f27709685e4266322e2690a,create,6419a3d0e6a450725f9b8205,task,2023-03-21 12:32:17.009,2023-03-21 12:32:17.009,"{""task"":{""_id"":""6419a3d0e6a450725f9b8205"",""content"":""test6""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,3,""
+1,6419a3e12bde1652d0f0879c,64132c94f0d59df1c9825ab8,6419a3e15f3fd8007098bd03,5f27709685e4266322e2690a,create,6419a3e15f3fd8007098bd03,task,2023-03-21 12:32:33.571,2023-03-21 12:32:33.571,"{""task"":{""_id"":""6419a3e15f3fd8007098bd03"",""content"":""test7""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,13,""
+1,6419a3f12bde1652d0f087ec,64132c94f0d59df1c9825ab8,6419a3d0e6a450725f9b8205,5f27709685e4266322e2690a,update.sprint,6419a3d0e6a450725f9b8205,task,2023-03-21 12:32:49.456,2023-03-21 12:32:49.456,"{""sprint"":{""_id"":""641889b4547467946c9ad2c8"",""name"":""beta1.0""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,25,""
+1,6419a4152bde1652d0f08832,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,5f27709685e4266322e2690a,update.sprint,6419a35ff98ea19169bb4a83,task,2023-03-21 12:33:25.128,2023-03-21 12:33:25.128,"{""sprint"":{""_id"":""6419a3fe514a20109f89e557"",""name"":""beta2.0""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,28,""
+1,6419a4152bde1652d0f08833,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,5f27709685e4266322e2690a,update.sprint,6419a3c24bccff5385d90268,task,2023-03-21 12:33:25.312,2023-03-21 12:33:25.312,"{""sprint"":{""_id"":""6419a3fe514a20109f89e557"",""name"":""beta2.0""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,29,""
+1,6419a4212bde1652d0f08860,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,5f27709685e4266322e2690a,update_startdate,6419a35ff98ea19169bb4a83,task,2023-03-21 12:33:37.209,2023-03-21 12:33:37.209,"{""startDate"":""2023-03-01T01:00:00.000Z"",""oldStartDate"":null,""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,43,""
+1,6419a426875ec8661de1bee7,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,5f27709685e4266322e2690a,update_duedate,6419a35ff98ea19169bb4a83,task,2023-03-21 12:33:42.511,2023-03-21 12:33:42.511,"{""oldDueDate"":null,""actionVersion"":2,""dueDate"":""2023-03-31T10:00:00.000Z""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,49,""
+1,6419a42b2bde1652d0f08884,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,5f27709685e4266322e2690a,update.taskflowstatus,6419a35ff98ea19169bb4a83,task,2023-03-21 12:33:47.265,2023-03-21 12:33:47.265,"{""isDone"":false,""taskflowstatus"":""已解决"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,53,""
+1,6419a42f875ec8661de1bf17,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,5f27709685e4266322e2690a,add.tag,6419a35ff98ea19169bb4a83,task,2023-03-21 12:33:51.569,2023-03-21 12:33:51.569,"{""tag"":""标签2""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,55,""
+1,6419a43f2bde1652d0f088bc,64132c94f0d59df1c9825ab8,64188f3e7e30eb94d86f8792,5f27709685e4266322e2690a,update.sprint,64188f3e7e30eb94d86f8792,task,2023-03-21 12:34:07.063,2023-03-21 12:34:07.063,"{""sprint"":{""_id"":""6419a406fbb99df0501fef07"",""name"":""beta3.0""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,26,""
+1,6419a43f875ec8661de1bf3f,64132c94f0d59df1c9825ab8,6419a357bf79590a54dd3a28,5f27709685e4266322e2690a,update.sprint,6419a357bf79590a54dd3a28,task,2023-03-21 12:34:07.066,2023-03-21 12:34:07.066,"{""sprint"":{""_id"":""6419a406fbb99df0501fef07"",""name"":""beta3.0""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,38,""
+1,6419a457875ec8661de1bf8f,64132c94f0d59df1c9825ab8,6419a357bf79590a54dd3a28,5f27709685e4266322e2690a,update.taskflowstatus,6419a357bf79590a54dd3a28,task,2023-03-21 12:34:31.158,2023-03-21 12:34:31.158,"{""isDone"":false,""taskflowstatus"":""工作中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,45,""
+1,6419a466875ec8661de1bfc4,64132c94f0d59df1c9825ab8,6419a466f407a6bb9c9e31ae,5f27709685e4266322e2690a,create,6419a466f407a6bb9c9e31ae,task,2023-03-21 12:34:46.202,2023-03-21 12:34:46.202,"{""task"":{""_id"":""6419a466f407a6bb9c9e31ae"",""content"":""test7""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,15,""
+1,6419a4882bde1652d0f089a7,64132c94f0d59df1c9825ab8,6419a3d0e6a450725f9b8205,5f27709685e4266322e2690a,update.taskflowstatus,6419a3d0e6a450725f9b8205,task,2023-03-21 12:35:20.487,2023-03-21 12:35:20.487,"{""isDone"":false,""taskflowstatus"":""待处理"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,47,""
+1,6419a488875ec8661de1c026,64132c94f0d59df1c9825ab8,6419a3d0e6a450725f9b8205,5f27709685e4266322e2690a,update.scenariofieldconfigId,6419a3d0e6a450725f9b8205,task,2023-03-21 12:35:20.485,2023-03-21 12:35:20.485,"{""newSfcName"":""需求"",""oldSfcName"":""任务""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,41,""
+1,6419a49e2bde1652d0f08a12,64132c94f0d59df1c9825ab8,641889e2f98ea19169bab8dd,5f27709685e4266322e2690a,update.taskflowstatus,641889e2f98ea19169bab8dd,task,2023-03-21 12:35:42.949,2023-03-21 12:35:42.949,"{""actionVersion"":2,""isDone"":false,""taskflowstatus"":""开发中"",""oldTaskflowstatus"":""待处理""}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,39,""
+1,6419aee0875ec8661de1e7a5,64132c94f0d59df1c9825ab8,6419aee0762f31f9b2168ca3,5f27709685e4266322e2690a,create,6419aee0762f31f9b2168ca3,task,2023-03-21 13:19:28.323,2023-03-21 13:19:28.323,"{""task"":{""_id"":""6419aee0762f31f9b2168ca3"",""content"":""bug1""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,11,""
+1,6419aee4875ec8661de1e7b0,64132c94f0d59df1c9825ab8,6419aee421643c55d9d1117f,5f27709685e4266322e2690a,create,6419aee421643c55d9d1117f,task,2023-03-21 13:19:32.860,2023-03-21 13:19:32.860,"{""task"":{""content"":""bug2"",""_id"":""6419aee421643c55d9d1117f""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,16,""
+1,6419aeeb2bde1652d0f0b0fb,64132c94f0d59df1c9825ab8,6419aeeb1502a928dbcdb66e,5f27709685e4266322e2690a,create,6419aeeb1502a928dbcdb66e,task,2023-03-21 13:19:39.863,2023-03-21 13:19:39.863,"{""task"":{""content"":""bug3"",""_id"":""6419aeeb1502a928dbcdb66e""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,20,""
+1,6419b165875ec8661de1eee8,64132c94f0d59df1c9825ab8,6419b1654bccff5385d90590,5f27709685e4266322e2690a,create,6419b1654bccff5385d90590,task,2023-03-21 13:30:13.335,2023-03-21 13:30:13.335,"{""task"":{""content"":""bug4"",""_id"":""6419b1654bccff5385d90590""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,14,""
+1,6419b16f875ec8661de1ef1f,64132c94f0d59df1c9825ab8,6419b16f7a4d42ee8e9246db,5f27709685e4266322e2690a,create,6419b16f7a4d42ee8e9246db,task,2023-03-21 13:30:23.404,2023-03-21 13:30:23.404,"{""task"":{""_id"":""6419b16f7a4d42ee8e9246db"",""content"":""bug5""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,22,""
+1,6419b1742bde1652d0f0b78b,64132c94f0d59df1c9825ab8,6419b17472707d4d15e64f86,5f27709685e4266322e2690a,create,6419b17472707d4d15e64f86,task,2023-03-21 13:30:28.852,2023-03-21 13:30:28.852,"{""task"":{""_id"":""6419b17472707d4d15e64f86"",""content"":""bug6""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,12,""
+1,6419b17c875ec8661de1ef45,64132c94f0d59df1c9825ab8,6419aee0762f31f9b2168ca3,5f27709685e4266322e2690a,update.taskflowstatus,6419aee0762f31f9b2168ca3,task,2023-03-21 13:30:36.102,2023-03-21 13:30:36.102,"{""isDone"":false,""taskflowstatus"":""修复中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,32,""
+1,6419b17f2bde1652d0f0b7a4,64132c94f0d59df1c9825ab8,6419aee421643c55d9d1117f,5f27709685e4266322e2690a,update.taskflowstatus,6419aee421643c55d9d1117f,task,2023-03-21 13:30:39.966,2023-03-21 13:30:39.966,"{""isDone"":false,""taskflowstatus"":""已解决"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,34,""
+1,6419b183875ec8661de1ef58,64132c94f0d59df1c9825ab8,6419aeeb1502a928dbcdb66e,5f27709685e4266322e2690a,update.taskflowstatus,6419aeeb1502a928dbcdb66e,task,2023-03-21 13:30:43.055,2023-03-21 13:30:43.055,"{""taskflowstatus"":""已拒绝"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,37,""
+1,6419b196875ec8661de1ef8d,64132c94f0d59df1c9825ab8,6419b17472707d4d15e64f86,5f27709685e4266322e2690a,update.taskflowstatus,6419b17472707d4d15e64f86,task,2023-03-21 13:31:02.025,2023-03-21 13:31:02.025,"{""taskflowstatus"":""修复中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,33,""
+1,6419b1b5875ec8661de1efe3,64132c94f0d59df1c9825ab8,6419b1b54ed7d8c44b411ba6,5f27709685e4266322e2690a,create,6419b1b54ed7d8c44b411ba6,task,2023-03-21 13:31:33.699,2023-03-21 13:31:33.699,"{""task"":{""_id"":""6419b1b54ed7d8c44b411ba6"",""content"":""xuqiu1""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,19,""
+1,6419b1c1875ec8661de1effa,64132c94f0d59df1c9825ab8,6419b1c1640380c7aecefe0e,5f27709685e4266322e2690a,create,6419b1c1640380c7aecefe0e,task,2023-03-21 13:31:45.093,2023-03-21 13:31:45.093,"{""task"":{""_id"":""6419b1c1640380c7aecefe0e"",""content"":""fasdf""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,10,""
+1,6419b1c82bde1652d0f0b861,64132c94f0d59df1c9825ab8,6419b1c8090e699c15cb72ee,5f27709685e4266322e2690a,create,6419b1c8090e699c15cb72ee,task,2023-03-21 13:31:52.612,2023-03-21 13:31:52.612,"{""task"":{""_id"":""6419b1c8090e699c15cb72ee"",""content"":""fasdfasd""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,18,""
+1,6419b1da875ec8661de1f042,64132c94f0d59df1c9825ab8,6419b1dabf79590a54dd3d75,5f27709685e4266322e2690a,create,6419b1dabf79590a54dd3d75,task,2023-03-21 13:32:10.308,2023-03-21 13:32:10.308,"{""task"":{""_id"":""6419b1dabf79590a54dd3d75"",""content"":""fasdzvaerrw""}}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,9,""
+1,6419b1e02bde1652d0f0b8a7,64132c94f0d59df1c9825ab8,6419a3d0e6a450725f9b8205,5f27709685e4266322e2690a,update.taskflowstatus,6419a3d0e6a450725f9b8205,task,2023-03-21 13:32:16.576,2023-03-21 13:32:16.576,"{""taskflowstatus"":""开发中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,51,""
+1,6419b1e32bde1652d0f0b8aa,64132c94f0d59df1c9825ab8,6419b1b54ed7d8c44b411ba6,5f27709685e4266322e2690a,update.taskflowstatus,6419b1b54ed7d8c44b411ba6,task,2023-03-21 13:32:19.225,2023-03-21 13:32:19.225,"{""isDone"":false,""taskflowstatus"":""已完成"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,36,""
+1,6419b1e82bde1652d0f0b8bf,64132c94f0d59df1c9825ab8,6419b1dabf79590a54dd3d75,5f27709685e4266322e2690a,update.taskflowstatus,6419b1dabf79590a54dd3d75,task,2023-03-21 13:32:24.654,2023-03-21 13:32:24.654,"{""isDone"":false,""taskflowstatus"":""测试中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,30,""
+1,6419b1ef875ec8661de1f070,64132c94f0d59df1c9825ab8,6419b1c1640380c7aecefe0e,5f27709685e4266322e2690a,update.taskflowstatus,6419b1c1640380c7aecefe0e,task,2023-03-21 13:32:31.063,2023-03-21 13:32:31.063,"{""isDone"":false,""taskflowstatus"":""开发中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,31,""
+1,6419b1f2875ec8661de1f077,64132c94f0d59df1c9825ab8,6419b1c8090e699c15cb72ee,5f27709685e4266322e2690a,update.taskflowstatus,6419b1c8090e699c15cb72ee,task,2023-03-21 13:32:34.026,2023-03-21 13:32:34.026,"{""isDone"":false,""taskflowstatus"":""测试中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,35,""
+1,6419b2122bde1652d0f0b919,64132c94f0d59df1c9825ab8,6419b1c1640380c7aecefe0e,5f27709685e4266322e2690a,update.taskflowstatus,6419b1c1640380c7aecefe0e,task,2023-03-21 13:33:06.678,2023-03-21 13:33:06.678,"{""isDone"":false,""taskflowstatus"":""已完成"",""oldTaskflowstatus"":""开发中"",""actionVersion"":2}",2023-03-23 14:24:52.988,2023-03-23 14:24:52.988,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,44,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_flow_status.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_flow_status.csv
new file mode 100644
index 000000000..2bedcd8c3
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_flow_status.csv
@@ -0,0 +1,31 @@
+connection_id,project_id,id,name,pos,taskflow_id,reject_status_ids,kind,creator_id,is_deleted,is_taskflowstatusruleexector,created,updated,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c94f0d59df1c9825ab8,64132c9461bb57f8d78a13a1,待处理,65536,64132c9461bb57f8d78a1396,"[""64132c9461bb57f8d78a13a4"",""64132c9461bb57f8d78a13a3"",""64132c9461bb57f8d78a13a2""]",start,5f27709685e4266322e2690a,0,0,2023-03-16 14:49:56.463,2023-03-16 14:49:56.728,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,1,""
+1,64132c94f0d59df1c9825ab8,64132c9461bb57f8d78a13a2,关闭,327680,64132c9461bb57f8d78a1396,[],end,5f27709685e4266322e2690a,0,0,2023-03-16 14:49:56.463,2023-03-16 14:49:56.765,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,27,""
+1,64132c94f0d59df1c9825ab8,64132c9461bb57f8d78a13a3,已解决,98304,64132c9461bb57f8d78a1396,"[""64132c9461bb57f8d78a13a2""]",end,5f27709685e4266322e2690a,0,0,2023-03-16 14:49:56.464,2023-03-16 14:49:56.783,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,8,""
+1,64132c94f0d59df1c9825ab8,64132c9461bb57f8d78a13a4,修复中,131072,64132c9461bb57f8d78a1396,"[""64132c9461bb57f8d78a13a3"",""64132c9461bb57f8d78a13a2""]",unset,5f27709685e4266322e2690a,0,0,2023-03-16 14:49:56.464,2023-03-21 12:28:29.096,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,10,""
+1,64132c94f0d59df1c9825ab8,641710e39039516d00cecc42,工作中,393216,64132c9461bb57f8d78a1396,[],unset,5f27709685e4266322e2690a,0,0,2023-03-19 13:40:51.972,2023-03-19 13:40:52.116,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,30,""
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de925302,待处理,65536,641889697ceb3c43de925300,"[""641889697ceb3c43de925306"",""641889697ceb3c43de925308"",""641889697ceb3c43de925304"",""641889697ceb3c43de92530a"",""641889697ceb3c43de92530c""]",start,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:21.337,2023-03-20 16:27:21.348,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,2,""
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de925304,已拒绝,262144,641889697ceb3c43de925300,"[""641889697ceb3c43de92530a"",""641889697ceb3c43de92530c""]",end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:21.339,2023-03-20 16:27:21.350,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,22,""
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de925306,重新打开,98304,641889697ceb3c43de925300,"[""641889697ceb3c43de925302"",""641889697ceb3c43de925308""]",start,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:21.341,2023-03-20 16:27:21.351,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,9,""
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de925308,修复中,131072,641889697ceb3c43de925300,"[""641889697ceb3c43de92530a"",""641889697ceb3c43de92530c"",""641889697ceb3c43de925304""]",unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:21.343,2023-03-20 16:27:21.353,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,11,""
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de92530a,已解决,196608,641889697ceb3c43de925300,"[""641889697ceb3c43de925304"",""641889697ceb3c43de92530c""]",end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:21.344,2023-03-20 16:27:21.354,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,17,""
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de92530c,关闭,327680,641889697ceb3c43de925300,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:21.346,2023-03-20 16:27:21.355,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,28,""
+1,64132c94f0d59df1c9825ab8,6418896b14f802bb89ad0e02,测试中,196608,6418896b14f802bb89ad0e00,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:23.213,2023-03-20 16:27:23.213,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,18,""
+1,64132c94f0d59df1c9825ab8,6418896b14f802bb89ad0e04,开发中,131072,6418896b14f802bb89ad0e00,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:23.215,2023-03-20 16:27:23.215,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,12,""
+1,64132c94f0d59df1c9825ab8,6418896b14f802bb89ad0e06,已完成,262144,6418896b14f802bb89ad0e00,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:23.218,2023-03-20 16:27:23.218,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,23,""
+1,64132c94f0d59df1c9825ab8,6418896b14f802bb89ad0e08,待处理,65536,6418896b14f802bb89ad0e00,[],start,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:23.221,2023-03-20 16:27:23.221,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,3,""
+1,64132c94f0d59df1c9825ab8,6418896e7bd38f77c2991be6,待评审,65536,6418896e7bd38f77c2991be4,[],start,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:26.966,2023-03-20 16:27:26.966,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,4,""
+1,64132c94f0d59df1c9825ab8,6418896e7bd38f77c2991be9,未执行,131072,6418896e7bd38f77c2991be4,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:26.971,2023-03-20 16:27:26.971,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,13,""
+1,64132c94f0d59df1c9825ab8,6418896e7bd38f77c2991bec,已通过,196608,6418896e7bd38f77c2991be4,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:26.975,2023-03-20 16:27:26.975,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,19,""
+1,64132c94f0d59df1c9825ab8,6418896e7bd38f77c2991bef,阻塞,262144,6418896e7bd38f77c2991be4,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:26.979,2023-03-20 16:27:26.979,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,24,""
+1,64132c94f0d59df1c9825ab8,6418896e7bd38f77c2991bf2,未通过,327680,6418896e7bd38f77c2991be4,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:26.982,2023-03-20 16:27:26.982,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,29,""
+1,64132c94f0d59df1c9825ab8,641889731ea118756f2a51be,待处理,65536,641889731ea118756f2a51bc,[],start,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:31.967,2023-03-20 16:27:31.967,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,5,""
+1,64132c94f0d59df1c9825ab8,641889731ea118756f2a51c4,已完成,131072,641889731ea118756f2a51bc,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:31.974,2023-03-20 16:27:31.974,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,14,""
+1,64132c94f0d59df1c9825ab8,641889759d2485e0cca44ab2,待处理,65536,641889759d2485e0cca44ab0,[],start,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:33.778,2023-03-20 16:27:33.778,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,6,""
+1,64132c94f0d59df1c9825ab8,641889759d2485e0cca44ab4,进行中,131072,641889759d2485e0cca44ab0,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:33.780,2023-03-20 16:27:33.780,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,15,""
+1,64132c94f0d59df1c9825ab8,641889759d2485e0cca44ab6,已完成,196608,641889759d2485e0cca44ab0,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:33.781,2023-03-20 16:27:33.781,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,20,""
+1,64132c94f0d59df1c9825ab8,641889759d2485e0cca44ab8,已取消,262144,641889759d2485e0cca44ab0,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:27:33.783,2023-03-20 16:27:33.783,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,25,""
+1,64132c94f0d59df1c9825ab8,64188fa389e95908d4c3bfca,待处理,65536,64188fa389e95908d4c3bfc3,[],start,5f27709685e4266322e2690a,0,0,2023-03-20 16:53:55.744,2023-03-20 16:53:55.744,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,7,""
+1,64132c94f0d59df1c9825ab8,64188fa389e95908d4c3bfdd,进行中,131072,64188fa389e95908d4c3bfc3,[],unset,5f27709685e4266322e2690a,0,0,2023-03-20 16:53:55.764,2023-03-20 16:53:55.764,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,16,""
+1,64132c94f0d59df1c9825ab8,64188fa389e95908d4c3bfe0,已完成,196608,64188fa389e95908d4c3bfc3,[],end,5f27709685e4266322e2690a,0,0,2023-03-20 16:53:55.772,2023-03-20 16:53:55.772,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,21,""
+1,64132c94f0d59df1c9825ab8,64193f2930f1deaea3ac9303,修复中,262144,64188fa389e95908d4c3bfc3,[],start,5f27709685e4266322e2690a,0,0,2023-03-21 05:22:49.451,2023-03-21 05:22:49.451,2023-03-23 14:25:03.130,2023-03-23 14:25:03.130,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_flow_status,26,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_scenarios.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_scenarios.csv
new file mode 100644
index 000000000..f19b4c01d
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_scenarios.csv
@@ -0,0 +1,7 @@
+connection_id,project_id,id,name,icon,type,scenariofields,creator_id,source,bound_to_object_id,bound_to_object_type,taskflow_id,is_archived,created,updated,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c94f0d59df1c9825ab8,64132c9461bb57f8d78a13b9,任务,task,default,"[{""customfieldId"":"""",""fieldType"":""sprint"",""required"":false},{""customfieldId"":"""",""fieldType"":""note"",""required"":false},{""customfieldId"":"""",""fieldType"":""priority"",""required"":false},{""customfieldId"":"""",""fieldType"":""tag"",""required"":false},{""customfieldId"":""641491d64bccff5385d74102"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":""641491d621643c55d9cf3f82"",""f [...]
+1,64132c94f0d59df1c9825ab8,641889697ceb3c43de925315,缺陷,bug,normal,"[{""customfieldId"":"""",""fieldType"":""sprint"",""required"":false},{""customfieldId"":"""",""fieldType"":""note"",""required"":true},{""customfieldId"":"""",""fieldType"":""storyPoint"",""required"":false},{""customfieldId"":"""",""fieldType"":""priority"",""required"":false},{""customfieldId"":""641889697a4d42ee8e91adeb"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":""641889694da203eb515c2047"" [...]
+1,64132c94f0d59df1c9825ab8,6418896b14f802bb89ad0e0b,需求,requirement,normal,"[{""customfieldId"":"""",""fieldType"":""sprint"",""required"":false},{""customfieldId"":"""",""fieldType"":""note"",""required"":false},{""customfieldId"":"""",""fieldType"":""storyPoint"",""required"":false},{""customfieldId"":"""",""fieldType"":""priority"",""required"":false},{""customfieldId"":""6418896b70a2e66184e84629"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":"""",""fieldType"": [...]
+1,64132c94f0d59df1c9825ab8,641889731ea118756f2a51b6,里程碑,milestone,normal,"[{""customfieldId"":"""",""fieldType"":""sprint"",""required"":false},{""customfieldId"":"""",""fieldType"":""note"",""required"":false},{""customfieldId"":"""",""fieldType"":""priority"",""required"":false},{""customfieldId"":"""",""fieldType"":""tag"",""required"":false},{""customfieldId"":""641491d64bccff5385d74102"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":""641491d621643c55d9cf3f82" [...]
+1,64132c94f0d59df1c9825ab8,641889759d2485e0cca44abc,风险,risk,normal,"[{""customfieldId"":"""",""fieldType"":""sprint"",""required"":false},{""customfieldId"":"""",""fieldType"":""note"",""required"":false},{""customfieldId"":""64188975a509e6c751123fc4"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":""64188975a06dd138358aa586"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":""64188975a509e6c751123fc5"",""fieldType"":""customfield"",""required"": [...]
+1,64132c94f0d59df1c9825ab8,64188fa389e95908d4c3bfaf,测试任务类型,subtask,normal,"[{""customfieldId"":"""",""fieldType"":""sprint"",""required"":false},{""customfieldId"":"""",""fieldType"":""note"",""required"":false},{""customfieldId"":"""",""fieldType"":""priority"",""required"":false},{""customfieldId"":"""",""fieldType"":""tag"",""required"":false},{""customfieldId"":""641491d64bccff5385d74102"",""fieldType"":""customfield"",""required"":false},{""customfieldId"":""641491d621643c55d9cf3f82 [...]
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_tag_tasks.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_tag_tasks.csv
new file mode 100644
index 000000000..630c2e5bf
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_tag_tasks.csv
@@ -0,0 +1,9 @@
+connection_id,project_id,task_id,task_tag_id,name,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,64152b54ed4ba1c701025878,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,1,""
+1,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,64157abbd068d1a90d907fd4,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,2,""
+1,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,6419a37873f833cc51a976a0,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,8,""
+1,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,6419a372a6216665fa0ecd16,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,""
+1,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,6419a37873f833cc51a976a0,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,""
+1,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,6419a37dc40b4a3162675583,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,""
+1,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,6419a383282bbf185553fdfe,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,""
+1,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,6419a38c589562163cb29de7,"",2023-03-23 14:24:40.428,2023-03-23 14:24:40.428,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_tags.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_tags.csv
new file mode 100644
index 000000000..a01c09e62
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_tags.csv
@@ -0,0 +1,8 @@
+connection_id,id,creator_id,project_id,organization_id,name,color,is_archived,created,updated,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64152b54ed4ba1c701025878,"",64132c94f0d59df1c9825ab8,"",bug,"",0,2023-03-18 03:09:08.727,2023-03-18 03:09:08.723,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,1,""
+1,64157abbd068d1a90d907fd4,"",64132c94f0d59df1c9825ab8,"",story,"",0,2023-03-18 08:47:55.785,2023-03-18 08:47:55.783,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,2,""
+1,6419a372a6216665fa0ecd16,"",64132c94f0d59df1c9825ab8,"",标签1,"",0,2023-03-21 12:30:42.206,2023-03-21 12:30:42.204,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,3,""
+1,6419a37873f833cc51a976a0,"",64132c94f0d59df1c9825ab8,"",标签2,"",0,2023-03-21 12:30:48.274,2023-03-21 12:30:48.272,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,4,""
+1,6419a37dc40b4a3162675583,"",64132c94f0d59df1c9825ab8,"",标签3,"",0,2023-03-21 12:30:53.950,2023-03-21 12:30:53.948,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,5,""
+1,6419a383282bbf185553fdfe,"",64132c94f0d59df1c9825ab8,"",标签5,"",0,2023-03-21 12:30:59.850,2023-03-21 12:30:59.848,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,6,""
+1,6419a38c589562163cb29de7,"",64132c94f0d59df1c9825ab8,"",标签,"",0,2023-03-21 12:31:08.166,2023-03-21 12:31:08.165,2023-03-23 14:24:41.683,2023-03-23 14:24:41.683,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_tags,7,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_worktime.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_worktime.csv
new file mode 100644
index 000000000..74330bbf7
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_task_worktime.csv
@@ -0,0 +1,3 @@
+connection_id,project_id,task_id,worktime_id,object_type,object_id,worktime,user_id,date,description,org_id,submitter_id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,641679064214fd004249c7e8,task,64132c945f3fd80070965938,36000000,5f27709685e4266322e2690a,2023-03-19 00:00:00,"",640b1c30c933fd85bb11ca31,5f27709685e4266322e2690a,2023-03-19 02:52:54.645,2023-03-19 02:52:54.645,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_worktime,1,""
+1,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,64169f83c75a9900466c9d25,task,64132c945f3fd80070965938,5400000,5f27709685e4266322e2690a,2023-03-19 00:00:00,"",640b1c30c933fd85bb11ca31,5f27709685e4266322e2690a,2023-03-19 05:37:07.197,2023-03-19 05:37:07.197,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_worktime,2,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_tasks.csv b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_tasks.csv
new file mode 100644
index 000000000..44dc9fc8e
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/_tool_teambition_tasks.csv
@@ -0,0 +1,23 @@
+connection_id,project_id,id,content,note,ancestor_ids,parent_task_id,tfs_id,tasklist_id,stage_id,tag_ids,creator_id,executor_id,involve_members,priority,story_point,recurrence,is_done,is_archived,visible,unique_id,start_date,due_date,accomplish_time,created,updated,sfc_id,sprint_id,customfields,std_type,std_status,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,64132c94f0d59df1c9825ab8,64132c945f3fd80070965938,【示例】账号绑定失败11,"",[],"",64132c9461bb57f8d78a13a3,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,"[""64152b54ed4ba1c701025878""]",5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",0,"",,1,0,projectMembers,3,2023-03-17 01:00:00,2023-03-23 10:00:00,2023-03-19 05:20:02.571,2023-03-16 14:49:56.617,2023-03-19 17:31:53.174,64132c9461bb57f8d78a13b9,"","[{""cfId"":""5ec6001f852a6181e4028090"",""type"":""number"" [...]
+1,64132c94f0d59df1c9825ab8,64132c945f3fd80070965939,【示例】App 登录报错,"",[],"",641710e39039516d00cecc42,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,"[""64157abbd068d1a90d907fd4""]",5f27709685e4266322e2690a,"","[""5f27709685e4266322e2690a""]",0,"",,0,0,projectMembers,7,,,,2023-03-16 14:49:56.618,2023-03-20 16:30:04.465,64132c9461bb57f8d78a13b9,641889b4547467946c9ad2c8,"[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2" [...]
+1,64132c94f0d59df1c9825ab8,641889e2f98ea19169bab8dd,testt42rfawe,"",[],"",6418896b14f802bb89ad0e04,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,13,,0,0,projectMembers,8,,,,2023-03-20 16:29:22.116,2023-03-21 12:35:42.907,6418896b14f802bb89ad0e0b,641889b4547467946c9ad2c8,"[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2""},{"" [...]
+1,64132c94f0d59df1c9825ab8,64188f3e7e30eb94d86f8792,风险,"",[],"",641889759d2485e0cca44ab2,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,9,,,,2023-03-20 16:52:14.510,2023-03-21 12:34:07.032,641889759d2485e0cca44abc,6419a406fbb99df0501fef07,"[{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2""},{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""}]","","",2023- [...]
+1,64132c94f0d59df1c9825ab8,6419a2df90097a8c84c5b7b8,test1,"",[],"",64132c9461bb57f8d78a13a1,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,10,,,,2023-03-21 12:28:15.811,2023-03-21 12:28:15.882,64132c9461bb57f8d78a13b9,"",[],"","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_tea [...]
+1,64132c94f0d59df1c9825ab8,6419a2f9344ff5c7682abcc8,fsdfdf,"",[],"",64132c9461bb57f8d78a13a1,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,11,,,,2023-03-21 12:28:41.272,2023-03-21 12:28:41.356,64132c9461bb57f8d78a13b9,"",[],"","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_te [...]
+1,64132c94f0d59df1c9825ab8,6419a357bf79590a54dd3a28,test2,"",[],"",641710e39039516d00cecc42,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",2,"",,0,0,projectMembers,12,,,,2023-03-21 12:30:15.504,2023-03-21 12:34:31.117,64132c9461bb57f8d78a13b9,6419a406fbb99df0501fef07,"[{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2""},{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""}]","","",202 [...]
+1,64132c94f0d59df1c9825ab8,6419a35ff98ea19169bb4a83,test3,"",[],"",64132c9461bb57f8d78a13a3,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,"[""6419a37873f833cc51a976a0""]",5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,1,0,projectMembers,13,2023-03-01 01:00:00,2023-03-31 10:00:00,2023-03-21 12:33:47.290,2023-03-21 12:30:22.973,2023-03-21 12:33:51.542,64132c9461bb57f8d78a13b9,6419a3fe514a20109f89e557,"[{""cfId"":""641491d6344ff5c76828fc4e""," [...]
+1,64132c94f0d59df1c9825ab8,6419a3c24bccff5385d90268,test4,"",[],"",64132c9461bb57f8d78a13a1,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,"[""6419a38c589562163cb29de7"",""6419a383282bbf185553fdfe"",""6419a37dc40b4a3162675583"",""6419a37873f833cc51a976a0"",""6419a372a6216665fa0ecd16""]",5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",0,"",,0,0,projectMembers,14,,,,2023-03-21 12:32:02.850,2023-03-21 12:33:25.118,64132c9461bb57f8d78a13b9,6419a3fe514a20 [...]
+1,64132c94f0d59df1c9825ab8,6419a3d0e6a450725f9b8205,test6,"",[],"",6418896b14f802bb89ad0e04,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",0,"",,0,0,projectMembers,15,,,,2023-03-21 12:32:16.899,2023-03-21 13:32:16.511,6418896b14f802bb89ad0e0b,641889b4547467946c9ad2c8,"[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2""}]","","",202 [...]
+1,64132c94f0d59df1c9825ab8,6419a3e15f3fd8007098bd03,test7,"",[],"",64132c9461bb57f8d78a13a1,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,16,,,,2023-03-21 12:32:33.504,2023-03-21 12:32:33.568,64132c9461bb57f8d78a13b9,641889b4547467946c9ad2c8,"[{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2""}]","","",2 [...]
+1,64132c94f0d59df1c9825ab8,6419a466f407a6bb9c9e31ae,test7,"",[],"",64132c9461bb57f8d78a13a1,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,17,,,,2023-03-21 12:34:46.097,2023-03-21 12:34:46.203,64132c9461bb57f8d78a13b9,6419a406fbb99df0501fef07,[],"","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df [...]
+1,64132c94f0d59df1c9825ab8,6419aee0762f31f9b2168ca3,bug1,"",[],"",641889697ceb3c43de925308,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,18,,,,2023-03-21 13:19:28.260,2023-03-21 13:30:36.063,641889697ceb3c43de925315,"","[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""Organizati [...]
+1,64132c94f0d59df1c9825ab8,6419aee421643c55d9d1117f,bug2,"",[],"",641889697ceb3c43de92530a,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,1,0,projectMembers,19,,,2023-03-21 13:30:40.008,2023-03-21 13:19:32.761,2023-03-21 13:30:40.008,641889697ceb3c43de925315,"","[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""Connec [...]
+1,64132c94f0d59df1c9825ab8,6419aeeb1502a928dbcdb66e,bug3,"",[],"",641889697ceb3c43de925304,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,1,0,projectMembers,20,,,2023-03-21 13:30:43.083,2023-03-21 13:19:39.808,2023-03-21 13:30:43.083,641889697ceb3c43de925315,"","[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""Connec [...]
+1,64132c94f0d59df1c9825ab8,6419b1654bccff5385d90590,bug4,"",[],"",641889697ceb3c43de925302,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,21,,,,2023-03-21 13:30:13.211,2023-03-21 13:30:13.326,641889697ceb3c43de925315,"","[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""Organizati [...]
+1,64132c94f0d59df1c9825ab8,6419b16f7a4d42ee8e9246db,bug5,"",[],"",641889697ceb3c43de925302,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,22,,,,2023-03-21 13:30:23.337,2023-03-21 13:30:23.404,641889697ceb3c43de925315,"","[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""Organizati [...]
+1,64132c94f0d59df1c9825ab8,6419b17472707d4d15e64f86,bug6,"",[],"",641889697ceb3c43de925308,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,23,,,,2023-03-21 13:30:28.787,2023-03-21 13:31:01.979,641889697ceb3c43de925315,"","[{""cfId"":""641889697a4d42ee8e91adeb"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""Organizati [...]
+1,64132c94f0d59df1c9825ab8,6419b1b54ed7d8c44b411ba6,xuqiu1,"",[],"",6418896b14f802bb89ad0e06,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,3,,1,0,projectMembers,24,,,2023-03-21 13:32:19.250,2023-03-21 13:31:33.629,2023-03-21 13:32:19.250,6418896b14f802bb89ad0e0b,"","[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup""},{""cfId"":""641491d6a509e6c751111854"",""type"":""lookup2""},{""c [...]
+1,64132c94f0d59df1c9825ab8,6419b1c1640380c7aecefe0e,fasdf,"",[],"",6418896b14f802bb89ad0e06,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,1,0,projectMembers,25,,,2023-03-21 13:33:06.709,2023-03-21 13:31:44.997,2023-03-21 13:33:06.709,6418896b14f802bb89ad0e0b,"","[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""Conne [...]
+1,64132c94f0d59df1c9825ab8,6419b1c8090e699c15cb72ee,fasdfasd,"",[],"",6418896b14f802bb89ad0e02,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,26,,,,2023-03-21 13:31:52.371,2023-03-21 13:32:33.979,6418896b14f802bb89ad0e0b,"","[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup""}]","","",2023-03-23 14:24:40.399,2023-03-23 14:24:40.399,"{""ConnectionId"":1,""Organi [...]
+1,64132c94f0d59df1c9825ab8,6419b1dabf79590a54dd3d75,fasdzvaerrw,"",[],"",6418896b14f802bb89ad0e02,64132c9461bb57f8d78a139d,64132c9461bb57f8d78a13ac,[],5f27709685e4266322e2690a,5f27709685e4266322e2690a,"[""5f27709685e4266322e2690a""]",-10,"",,0,0,projectMembers,27,,,,2023-03-21 13:32:10.205,2023-03-21 13:32:24.611,6418896b14f802bb89ad0e0b,6419a3fe514a20109f89e557,"[{""cfId"":""6418896b70a2e66184e84629"",""type"":""commongroup""},{""cfId"":""641491d6344ff5c76828fc4e"",""type"":""lookup2""} [...]
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/board_issues.csv b/backend/plugins/teambition/e2e/snapshot_tables/board_issues.csv
new file mode 100644
index 000000000..67c2f7e63
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/board_issues.csv
@@ -0,0 +1,23 @@
+board_id,issue_id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:64132c945f3fd80070965938,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,1,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:64132c945f3fd80070965939,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,2,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:641889e2f98ea19169bab8dd,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,3,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:64188f3e7e30eb94d86f8792,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,4,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a2df90097a8c84c5b7b8,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,5,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a2f9344ff5c7682abcc8,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,6,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a357bf79590a54dd3a28,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,7,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,8,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a3c24bccff5385d90268,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,10,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a3e15f3fd8007098bd03,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,11,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419a466f407a6bb9c9e31ae,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,12,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419aee0762f31f9b2168ca3,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,13,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419aee421643c55d9d1117f,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,14,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419aeeb1502a928dbcdb66e,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,15,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b1654bccff5385d90590,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,16,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b16f7a4d42ee8e9246db,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,17,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b17472707d4d15e64f86,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,18,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b1b54ed7d8c44b411ba6,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,19,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b1c1640380c7aecefe0e,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,20,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b1c8090e699c15cb72ee,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,21,""
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionTask:1:6419b1dabf79590a54dd3d75,2023-03-23 14:25:04.266,2023-03-23 14:25:04.266,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,22,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/board_sprints.csv b/backend/plugins/teambition/e2e/snapshot_tables/board_sprints.csv
new file mode 100644
index 000000000..77147dbe8
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/board_sprints.csv
@@ -0,0 +1,4 @@
+created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,board_id,sprint_id
+2023-03-23 14:24:57.708,2023-03-23 14:24:57.708,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,1,"",teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionSprint:1:641889b4547467946c9ad2c8
+2023-03-23 14:24:57.708,2023-03-23 14:24:57.708,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,2,"",teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionSprint:1:6419a3fe514a20109f89e557
+2023-03-23 14:24:57.708,2023-03-23 14:24:57.708,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,3,"",teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,teambition:TeambitionSprint:1:6419a406fbb99df0501fef07
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/boards.csv b/backend/plugins/teambition/e2e/snapshot_tables/boards.csv
new file mode 100644
index 000000000..5f1b1d7cb
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/boards.csv
@@ -0,0 +1,2 @@
+id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,description,url,created_date,type
+teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8,2023-03-23 14:24:57.160,2023-03-23 14:24:57.160,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_projects,1,"",缺陷管理,"",https://www.teambition.com/project/64132c94f0d59df1c9825ab8,2023-03-16 14:49:56.415,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/issue_changelogs.csv b/backend/plugins/teambition/e2e/snapshot_tables/issue_changelogs.csv
new file mode 100644
index 000000000..a41827dc9
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/issue_changelogs.csv
@@ -0,0 +1,64 @@
+id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,issue_id,author_id,author_name,field_id,field_name,original_from_value,original_to_value,from_value,to_value,created_date
+teambition:TeambitionTaskActivity:1:64132c942bde1652d0ca4796,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,5,"",teambition:TeambitionTask:1:64132c945f3fd80070965939,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""content"":""【示例】App 登录报错"",""_id"":""64132c945f3fd80070965939""}}","","",2023-03-16 14:49:56.774
+teambition:TeambitionTaskActivity:1:64132c94875ec8661dbad223,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,2,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""content"":""【示例】账号绑定失败"",""_id"":""64132c945f3fd80070965938""}}","","",2023-03-16 14:49:56.756
+teambition:TeambitionTaskActivity:1:64152b552bde1652d0d590b0,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,24,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",add.tag,"","","{""tag"":""bug""}","","",2023-03-18 03:09:09.045
+teambition:TeambitionTaskActivity:1:64152beb2bde1652d0d59414,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,40,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",comment,"","","{""renderMode"":""text"",""attachments"":[],""dingFiles"":[],""comment"":""test"",""isOnlyNotifyMentions"":false,""isDingtal [...]
+teambition:TeambitionTaskActivity:1:641535ee2bde1652d0d5d56a,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,27,"",teambition:TeambitionTask:1:64132c945f3fd80070965939,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",comment,"","","{""isOnlyNotifyMentions"":false,""isDingtalkPM"":true,""renderMode"":""text"",""attachments"":[],""dingFiles"":[],""comment" [...]
+teambition:TeambitionTaskActivity:1:64153aab875ec8661dc699d2,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,46,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_content,"","","{""content"":""【示例】账号绑定失败11"",""oldContent"":""【示例】账号绑定失败""}","","",2023-03-18 04:14:35.700
+teambition:TeambitionTaskActivity:1:64157abb875ec8661dc7e2b0,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,42,"",teambition:TeambitionTask:1:64132c945f3fd80070965939,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",add.tag,"","","{""tag"":""story""}","","",2023-03-18 08:47:55.965
+teambition:TeambitionTaskActivity:1:6416742d875ec8661dc97f0c,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,50,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",comment,"","","{""comment"":""twst2"",""isOnlyNotifyMentions"":false,""isDingtalkPM"":true,""renderMode"":""text"",""attachments"":[],""din [...]
+teambition:TeambitionTaskActivity:1:64167724875ec8661dc98729,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,54,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",comment,"","","{""isOnlyNotifyMentions"":false,""isDingtalkPM"":true,""renderMode"":""text"",""attachments"":[],""dingFiles"":[],""comment" [...]
+teambition:TeambitionTaskActivity:1:641678fefadbca6b74267a64,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,56,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",standard,"","","{""title"":""提交了 3月19日 计划工时 14 小时"",""icon"":""stopwatch""}","","",2023-03-19 02:52:46.354
+teambition:TeambitionTaskActivity:1:64167906c40b4a3162d31e50,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,57,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",standard,"","","{""title"":""提交了 3月19日 实际工时 10 小时"",""subtitle"":"""",""icon"":""stopwatch""}","","",2023-03-19 02:52:54.707
+teambition:TeambitionTaskActivity:1:64169b822bde1652d0d91985,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,58,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false,""taskflowstatus"":""已解决""}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:64169f835538aa396dc8d13f,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,59,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",standard,"","","{""title"":""提交了 3月19日 实际工时 1.5 小时"",""subtitle"":"""",""icon"":""stopwatch""}","","",2023-03-19 05:37:07.262
+teambition:TeambitionTaskActivity:1:641710ed875ec8661dcb13f2,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,48,"",teambition:TeambitionTask:1:64132c945f3fd80070965939,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""taskflowstatus"":""工作中"",""oldTaskflowstatus"":""修复中"",""actionVersion"":2,""isDone"":false}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:641732f9875ec8661dcb8c4f,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,60,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_startdate,"","","{""startDate"":""2023-03-20T01:00:00.000Z"",""oldStartDate"":null,""actionVersion"":2}","","",2023-03-19 16:06:17.103
+teambition:TeambitionTaskActivity:1:641732fd2bde1652d0dac377,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,61,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_duedate,"","","{""oldDueDate"":null,""actionVersion"":2,""dueDate"":""2023-03-23T10:00:00.000Z""}","","",2023-03-19 16:06:21.293
+teambition:TeambitionTaskActivity:1:64173e0a2bde1652d0dacf00,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,62,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_executor,"","","{""_executorId"":""5f27709685e4266322e2690a"",""_oldExecutorId"":null,""actionVersion"":2}","","",2023-03-19 16:53:30.157
+teambition:TeambitionTaskActivity:1:641747092bde1652d0dadeb7,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,63,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_startdate,"","","{""actionVersion"":2,""startDate"":""2023-03-17T01:00:00.000Z"",""oldStartDate"":""2023-03-20T01:00:00.000Z""}","", [...]
+teambition:TeambitionTaskActivity:1:641889e22bde1652d0e6a927,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,1,"",teambition:TeambitionTask:1:641889e2f98ea19169bab8dd,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""641889e2f98ea19169bab8dd"",""content"":""testt42rfawe""}}","","",2023-03-20 16:29:22.229
+teambition:TeambitionTaskActivity:1:64188a0c875ec8661dd7ac7c,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,52,"",teambition:TeambitionTask:1:64132c945f3fd80070965939,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.sprint,"","","{""sprint"":{""_id"":""641889b4547467946c9ad2c8"",""name"":""beta1.0""}}","","",2023-03-20 16:30:04.507
+teambition:TeambitionTaskActivity:1:64188ea5875ec8661dd7b0f7,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,23,"",teambition:TeambitionTask:1:641889e2f98ea19169bab8dd,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",clear.customfield,"","","{""type"":""commongroup"",""name"":""需求分类"",""_customfieldId"":""6418896b70a2e66184e84629"",""value"":[],""values" [...]
+teambition:TeambitionTaskActivity:1:64188f3e2bde1652d0e6ae48,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,4,"",teambition:TeambitionTask:1:64188f3e7e30eb94d86f8792,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""64188f3e7e30eb94d86f8792"",""content"":""风险""}}","","",2023-03-20 16:52:14.585
+teambition:TeambitionTaskActivity:1:6419a2df2bde1652d0f083d2,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,7,"",teambition:TeambitionTask:1:6419a2df90097a8c84c5b7b8,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a2df90097a8c84c5b7b8"",""content"":""test1""}}","","",2023-03-21 12:28:15.923
+teambition:TeambitionTaskActivity:1:6419a2f9875ec8661de1bacd,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,17,"",teambition:TeambitionTask:1:6419a2f9344ff5c7682abcc8,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a2f9344ff5c7682abcc8"",""content"":""fsdfdf""}}","","",2023-03-21 12:28:41.443
+teambition:TeambitionTaskActivity:1:6419a3572bde1652d0f085c5,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,21,"",teambition:TeambitionTask:1:6419a357bf79590a54dd3a28,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a357bf79590a54dd3a28"",""content"":""test2""}}","","",2023-03-21 12:30:15.726
+teambition:TeambitionTaskActivity:1:6419a35f2bde1652d0f085e5,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,6,"",teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a35ff98ea19169bb4a83"",""content"":""test3""}}","","",2023-03-21 12:30:23.042
+teambition:TeambitionTaskActivity:1:6419a3c2875ec8661de1bdc2,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,8,"",teambition:TeambitionTask:1:6419a3c24bccff5385d90268,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a3c24bccff5385d90268"",""content"":""test4""}}","","",2023-03-21 12:32:02.925
+teambition:TeambitionTaskActivity:1:6419a3d1875ec8661de1bdf4,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,3,"",teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a3d0e6a450725f9b8205"",""content"":""test6""}}","","",2023-03-21 12:32:17.009
+teambition:TeambitionTaskActivity:1:6419a3e12bde1652d0f0879c,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,13,"",teambition:TeambitionTask:1:6419a3e15f3fd8007098bd03,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a3e15f3fd8007098bd03"",""content"":""test7""}}","","",2023-03-21 12:32:33.571
+teambition:TeambitionTaskActivity:1:6419a3f12bde1652d0f087ec,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,25,"",teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.sprint,"","","{""sprint"":{""_id"":""641889b4547467946c9ad2c8"",""name"":""beta1.0""}}","","",2023-03-21 12:32:49.456
+teambition:TeambitionTaskActivity:1:6419a4152bde1652d0f08832,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,28,"",teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.sprint,"","","{""sprint"":{""_id"":""6419a3fe514a20109f89e557"",""name"":""beta2.0""}}","","",2023-03-21 12:33:25.128
+teambition:TeambitionTaskActivity:1:6419a4152bde1652d0f08833,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,29,"",teambition:TeambitionTask:1:6419a3c24bccff5385d90268,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.sprint,"","","{""sprint"":{""_id"":""6419a3fe514a20109f89e557"",""name"":""beta2.0""}}","","",2023-03-21 12:33:25.312
+teambition:TeambitionTaskActivity:1:6419a4212bde1652d0f08860,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,43,"",teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_startdate,"","","{""startDate"":""2023-03-01T01:00:00.000Z"",""oldStartDate"":null,""actionVersion"":2}","","",2023-03-21 12:33:37.209
+teambition:TeambitionTaskActivity:1:6419a426875ec8661de1bee7,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,49,"",teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update_duedate,"","","{""oldDueDate"":null,""actionVersion"":2,""dueDate"":""2023-03-31T10:00:00.000Z""}","","",2023-03-21 12:33:42.511
+teambition:TeambitionTaskActivity:1:6419a42b2bde1652d0f08884,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,53,"",teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""已解决"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419a42f875ec8661de1bf17,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,55,"",teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",add.tag,"","","{""tag"":""标签2""}","","",2023-03-21 12:33:51.569
+teambition:TeambitionTaskActivity:1:6419a43f2bde1652d0f088bc,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,26,"",teambition:TeambitionTask:1:64188f3e7e30eb94d86f8792,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.sprint,"","","{""sprint"":{""_id"":""6419a406fbb99df0501fef07"",""name"":""beta3.0""}}","","",2023-03-21 12:34:07.063
+teambition:TeambitionTaskActivity:1:6419a43f875ec8661de1bf3f,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,38,"",teambition:TeambitionTask:1:6419a357bf79590a54dd3a28,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.sprint,"","","{""sprint"":{""_id"":""6419a406fbb99df0501fef07"",""name"":""beta3.0""}}","","",2023-03-21 12:34:07.066
+teambition:TeambitionTaskActivity:1:6419a457875ec8661de1bf8f,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,45,"",teambition:TeambitionTask:1:6419a357bf79590a54dd3a28,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""工作中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419a466875ec8661de1bfc4,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,15,"",teambition:TeambitionTask:1:6419a466f407a6bb9c9e31ae,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419a466f407a6bb9c9e31ae"",""content"":""test7""}}","","",2023-03-21 12:34:46.202
+teambition:TeambitionTaskActivity:1:6419a4882bde1652d0f089a7,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,47,"",teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""待处理"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419a488875ec8661de1c026,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,41,"",teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.scenariofieldconfigId,"","","{""newSfcName"":""需求"",""oldSfcName"":""任务""}","","",2023-03-21 12:35:20.485
+teambition:TeambitionTaskActivity:1:6419a49e2bde1652d0f08a12,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,39,"",teambition:TeambitionTask:1:641889e2f98ea19169bab8dd,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""actionVersion"":2,""isDone"":false,""taskflowstatus"":""开发中"",""oldTaskflowstatus"":""待处理""}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419aee0875ec8661de1e7a5,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,11,"",teambition:TeambitionTask:1:6419aee0762f31f9b2168ca3,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419aee0762f31f9b2168ca3"",""content"":""bug1""}}","","",2023-03-21 13:19:28.323
+teambition:TeambitionTaskActivity:1:6419aee4875ec8661de1e7b0,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,16,"",teambition:TeambitionTask:1:6419aee421643c55d9d1117f,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""content"":""bug2"",""_id"":""6419aee421643c55d9d1117f""}}","","",2023-03-21 13:19:32.860
+teambition:TeambitionTaskActivity:1:6419aeeb2bde1652d0f0b0fb,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,20,"",teambition:TeambitionTask:1:6419aeeb1502a928dbcdb66e,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""content"":""bug3"",""_id"":""6419aeeb1502a928dbcdb66e""}}","","",2023-03-21 13:19:39.863
+teambition:TeambitionTaskActivity:1:6419b165875ec8661de1eee8,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,14,"",teambition:TeambitionTask:1:6419b1654bccff5385d90590,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""content"":""bug4"",""_id"":""6419b1654bccff5385d90590""}}","","",2023-03-21 13:30:13.335
+teambition:TeambitionTaskActivity:1:6419b16f875ec8661de1ef1f,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,22,"",teambition:TeambitionTask:1:6419b16f7a4d42ee8e9246db,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419b16f7a4d42ee8e9246db"",""content"":""bug5""}}","","",2023-03-21 13:30:23.404
+teambition:TeambitionTaskActivity:1:6419b1742bde1652d0f0b78b,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,12,"",teambition:TeambitionTask:1:6419b17472707d4d15e64f86,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419b17472707d4d15e64f86"",""content"":""bug6""}}","","",2023-03-21 13:30:28.852
+teambition:TeambitionTaskActivity:1:6419b17c875ec8661de1ef45,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,32,"",teambition:TeambitionTask:1:6419aee0762f31f9b2168ca3,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""修复中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b17f2bde1652d0f0b7a4,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,34,"",teambition:TeambitionTask:1:6419aee421643c55d9d1117f,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""已解决"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b183875ec8661de1ef58,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,37,"",teambition:TeambitionTask:1:6419aeeb1502a928dbcdb66e,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""taskflowstatus"":""已拒绝"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b196875ec8661de1ef8d,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,33,"",teambition:TeambitionTask:1:6419b17472707d4d15e64f86,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""taskflowstatus"":""修复中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b1b5875ec8661de1efe3,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,19,"",teambition:TeambitionTask:1:6419b1b54ed7d8c44b411ba6,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419b1b54ed7d8c44b411ba6"",""content"":""xuqiu1""}}","","",2023-03-21 13:31:33.699
+teambition:TeambitionTaskActivity:1:6419b1c1875ec8661de1effa,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,10,"",teambition:TeambitionTask:1:6419b1c1640380c7aecefe0e,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419b1c1640380c7aecefe0e"",""content"":""fasdf""}}","","",2023-03-21 13:31:45.093
+teambition:TeambitionTaskActivity:1:6419b1c82bde1652d0f0b861,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,18,"",teambition:TeambitionTask:1:6419b1c8090e699c15cb72ee,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419b1c8090e699c15cb72ee"",""content"":""fasdfasd""}}","","",2023-03-21 13:31:52.612
+teambition:TeambitionTaskActivity:1:6419b1da875ec8661de1f042,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,9,"",teambition:TeambitionTask:1:6419b1dabf79590a54dd3d75,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",create,"","","{""task"":{""_id"":""6419b1dabf79590a54dd3d75"",""content"":""fasdzvaerrw""}}","","",2023-03-21 13:32:10.308
+teambition:TeambitionTaskActivity:1:6419b1e02bde1652d0f0b8a7,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,51,"",teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""taskflowstatus"":""开发中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2,""isDone"":false}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b1e32bde1652d0f0b8aa,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,36,"",teambition:TeambitionTask:1:6419b1b54ed7d8c44b411ba6,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""已完成"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b1e82bde1652d0f0b8bf,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,30,"",teambition:TeambitionTask:1:6419b1dabf79590a54dd3d75,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""测试中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b1ef875ec8661de1f070,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,31,"",teambition:TeambitionTask:1:6419b1c1640380c7aecefe0e,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""开发中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b1f2875ec8661de1f077,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,35,"",teambition:TeambitionTask:1:6419b1c8090e699c15cb72ee,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""测试中"",""oldTaskflowstatus"":""待处理"",""actionVersion"":2}","","",2023-03 [...]
+teambition:TeambitionTaskActivity:1:6419b2122bde1652d0f0b919,2023-03-23 14:24:53.061,2023-03-23 14:24:53.061,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,44,"",teambition:TeambitionTask:1:6419b1c1640380c7aecefe0e,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",update.taskflowstatus,"","","{""isDone"":false,""taskflowstatus"":""已完成"",""oldTaskflowstatus"":""开发中"",""actionVersion"":2}","","",2023-03 [...]
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/issue_comments.csv b/backend/plugins/teambition/e2e/snapshot_tables/issue_comments.csv
new file mode 100644
index 000000000..7a08cfb3d
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/issue_comments.csv
@@ -0,0 +1,5 @@
+id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,issue_id,body,account_id,created_date
+teambition:TeambitionTaskActivity:1:64152beb2bde1652d0d59414,2023-03-23 14:24:53.038,2023-03-23 14:24:53.038,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,40,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,test,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,2023-03-18 03:11:39.513
+teambition:TeambitionTaskActivity:1:641535ee2bde1652d0d5d56a,2023-03-23 14:24:53.038,2023-03-23 14:24:53.038,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,27,"",teambition:TeambitionTask:1:64132c945f3fd80070965939,test,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,2023-03-18 03:54:22.963
+teambition:TeambitionTaskActivity:1:6416742d875ec8661dc97f0c,2023-03-23 14:24:53.038,2023-03-23 14:24:53.038,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,50,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,twst2,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,2023-03-19 02:32:13.287
+teambition:TeambitionTaskActivity:1:64167724875ec8661dc98729,2023-03-23 14:24:53.038,2023-03-23 14:24:53.038,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_activities,54,"",teambition:TeambitionTask:1:64132c945f3fd80070965938,423534,teambition:TeambitionAccount:1:5f27709685e4266322e2690a,2023-03-19 02:44:52.763
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/issue_labels.csv b/backend/plugins/teambition/e2e/snapshot_tables/issue_labels.csv
new file mode 100644
index 000000000..15f5234f0
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/issue_labels.csv
@@ -0,0 +1,9 @@
+issue_id,label_name,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+teambition:TeambitionTask:1:64132c945f3fd80070965938,bug,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:64132c945f3fd80070965939,story,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,标签2,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:6419a3c24bccff5385d90268,标签,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:6419a3c24bccff5385d90268,标签1,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:6419a3c24bccff5385d90268,标签2,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:6419a3c24bccff5385d90268,标签3,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
+teambition:TeambitionTask:1:6419a3c24bccff5385d90268,标签5,2023-03-23 14:24:41.728,2023-03-23 14:24:41.728,"","",0,""
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/issue_worklogs.csv b/backend/plugins/teambition/e2e/snapshot_tables/issue_worklogs.csv
new file mode 100644
index 000000000..30642934c
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/issue_worklogs.csv
@@ -0,0 +1,3 @@
+id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,author_id,comment,time_spent_minutes,logged_date,started_date,issue_id
+teambition:TeambitionTaskWorktime:1:641679064214fd004249c7e8,2023-03-23 14:24:56.972,2023-03-23 14:24:56.972,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_worktime,1,"",teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",600,2023-03-19 02:52:54.645,2023-03-19 00:00:00,teambition:TeambitionTask:1:64132c945f3fd80070965938
+teambition:TeambitionTaskWorktime:1:64169f83c75a9900466c9d25,2023-03-23 14:24:56.972,2023-03-23 14:24:56.972,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_task_worktime,2,"",teambition:TeambitionAccount:1:5f27709685e4266322e2690a,"",90,2023-03-19 05:37:07.197,2023-03-19 00:00:00,teambition:TeambitionTask:1:64132c945f3fd80070965938
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/issues.csv b/backend/plugins/teambition/e2e/snapshot_tables/issues.csv
new file mode 100644
index 000000000..80bbe8dc5
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/issues.csv
@@ -0,0 +1,23 @@
+id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,url,icon_url,issue_key,title,description,epic_key,type,status,original_status,resolution_date,created_date,updated_date,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component,lead_time_minutes,original_project,original_type,story_point
+teambition:TeambitionTask:1:64132c945f3fd80070965938,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,1,"",https://www.teambition.com/task/64132c945f3fd80070965938,"",64132c945f3fd80070965938,【示例】账号绑定失败11,"","","",DONE,已解决,2023-03-19 05:20:02.571,2023-03-16 14:49:56.617,2023-03-19 17:31:53.174,"",0,9180,3140,6040,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,col [...]
+teambition:TeambitionTask:1:64132c945f3fd80070965939,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,2,"",https://www.teambition.com/task/64132c945f3fd80070965939,"",64132c945f3fd80070965939,【示例】App 登录报错,"","","",IN_PROGRESS,工作中,,2023-03-16 14:49:56.618,2023-03-20 16:30:04.465,"",0,0,0,0,5f27709685e4266322e2690a,coldgust,"","","","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:641889e2f98ea19169bab8dd,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,3,"",https://www.teambition.com/task/641889e2f98ea19169bab8dd,"",641889e2f98ea19169bab8dd,testt42rfawe,"","",REQUIREMENT,IN_PROGRESS,开发中,,2023-03-20 16:29:22.116,2023-03-21 12:35:42.907,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0, [...]
+teambition:TeambitionTask:1:64188f3e7e30eb94d86f8792,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,4,"",https://www.teambition.com/task/64188f3e7e30eb94d86f8792,"",64188f3e7e30eb94d86f8792,风险,"","",INCIDENT,TODO,待处理,,2023-03-20 16:52:14.510,2023-03-21 12:34:07.032,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,风险,0
+teambition:TeambitionTask:1:6419a2df90097a8c84c5b7b8,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,5,"",https://www.teambition.com/task/6419a2df90097a8c84c5b7b8,"",6419a2df90097a8c84c5b7b8,test1,"","","",TODO,待处理,,2023-03-21 12:28:15.811,2023-03-21 12:28:15.882,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:6419a2f9344ff5c7682abcc8,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,6,"",https://www.teambition.com/task/6419a2f9344ff5c7682abcc8,"",6419a2f9344ff5c7682abcc8,fsdfdf,"","","",TODO,待处理,,2023-03-21 12:28:41.272,2023-03-21 12:28:41.356,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:6419a357bf79590a54dd3a28,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,7,"",https://www.teambition.com/task/6419a357bf79590a54dd3a28,"",6419a357bf79590a54dd3a28,test2,"","","",IN_PROGRESS,工作中,,2023-03-21 12:30:15.504,2023-03-21 12:34:31.117,"",2,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,8,"",https://www.teambition.com/task/6419a35ff98ea19169bb4a83,"",6419a35ff98ea19169bb4a83,test3,"","","",DONE,已解决,2023-03-21 12:33:47.290,2023-03-21 12:30:22.973,2023-03-21 12:33:51.542,"",-10,43740,29493,14247,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldg [...]
+teambition:TeambitionTask:1:6419a3c24bccff5385d90268,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,"",https://www.teambition.com/task/6419a3c24bccff5385d90268,"",6419a3c24bccff5385d90268,test4,"","","",TODO,待处理,,2023-03-21 12:32:02.850,2023-03-21 12:33:25.118,"",0,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,10,"",https://www.teambition.com/task/6419a3d0e6a450725f9b8205,"",6419a3d0e6a450725f9b8205,test6,"","",REQUIREMENT,IN_PROGRESS,开发中,,2023-03-21 12:32:16.899,2023-03-21 13:32:16.511,"",0,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,需求,0
+teambition:TeambitionTask:1:6419a3e15f3fd8007098bd03,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,11,"",https://www.teambition.com/task/6419a3e15f3fd8007098bd03,"",6419a3e15f3fd8007098bd03,test7,"","","",TODO,待处理,,2023-03-21 12:32:33.504,2023-03-21 12:32:33.568,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:6419a466f407a6bb9c9e31ae,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,12,"",https://www.teambition.com/task/6419a466f407a6bb9c9e31ae,"",6419a466f407a6bb9c9e31ae,test7,"","","",TODO,待处理,,2023-03-21 12:34:46.097,2023-03-21 12:34:46.203,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,任务,0
+teambition:TeambitionTask:1:6419aee0762f31f9b2168ca3,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,13,"",https://www.teambition.com/task/6419aee0762f31f9b2168ca3,"",6419aee0762f31f9b2168ca3,bug1,"","",BUG,IN_PROGRESS,修复中,,2023-03-21 13:19:28.260,2023-03-21 13:30:36.063,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,缺陷,0
+teambition:TeambitionTask:1:6419aee421643c55d9d1117f,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,14,"",https://www.teambition.com/task/6419aee421643c55d9d1117f,"",6419aee421643c55d9d1117f,bug2,"","",BUG,DONE,已解决,2023-03-21 13:30:40.008,2023-03-21 13:19:32.761,2023-03-21 13:30:40.008,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0 [...]
+teambition:TeambitionTask:1:6419aeeb1502a928dbcdb66e,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,15,"",https://www.teambition.com/task/6419aeeb1502a928dbcdb66e,"",6419aeeb1502a928dbcdb66e,bug3,"","",BUG,DONE,已拒绝,2023-03-21 13:30:43.083,2023-03-21 13:19:39.808,2023-03-21 13:30:43.083,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0 [...]
+teambition:TeambitionTask:1:6419b1654bccff5385d90590,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,16,"",https://www.teambition.com/task/6419b1654bccff5385d90590,"",6419b1654bccff5385d90590,bug4,"","",BUG,TODO,待处理,,2023-03-21 13:30:13.211,2023-03-21 13:30:13.326,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,缺陷,0
+teambition:TeambitionTask:1:6419b16f7a4d42ee8e9246db,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,17,"",https://www.teambition.com/task/6419b16f7a4d42ee8e9246db,"",6419b16f7a4d42ee8e9246db,bug5,"","",BUG,TODO,待处理,,2023-03-21 13:30:23.337,2023-03-21 13:30:23.404,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,缺陷,0
+teambition:TeambitionTask:1:6419b17472707d4d15e64f86,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,18,"",https://www.teambition.com/task/6419b17472707d4d15e64f86,"",6419b17472707d4d15e64f86,bug6,"","",BUG,IN_PROGRESS,修复中,,2023-03-21 13:30:28.787,2023-03-21 13:31:01.979,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,缺陷,0
+teambition:TeambitionTask:1:6419b1b54ed7d8c44b411ba6,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,19,"",https://www.teambition.com/task/6419b1b54ed7d8c44b411ba6,"",6419b1b54ed7d8c44b411ba6,xuqiu1,"","",REQUIREMENT,DONE,已完成,2023-03-21 13:32:19.250,2023-03-21 13:31:33.629,2023-03-21 13:32:19.250,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgu [...]
+teambition:TeambitionTask:1:6419b1c1640380c7aecefe0e,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,20,"",https://www.teambition.com/task/6419b1c1640380c7aecefe0e,"",6419b1c1640380c7aecefe0e,fasdf,"","",REQUIREMENT,DONE,已完成,2023-03-21 13:33:06.709,2023-03-21 13:31:44.997,2023-03-21 13:33:06.709,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgus [...]
+teambition:TeambitionTask:1:6419b1c8090e699c15cb72ee,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,21,"",https://www.teambition.com/task/6419b1c8090e699c15cb72ee,"",6419b1c8090e699c15cb72ee,fasdfasd,"","",REQUIREMENT,IN_PROGRESS,测试中,,2023-03-21 13:31:52.371,2023-03-21 13:32:33.979,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0,缺陷管理,需求,0
+teambition:TeambitionTask:1:6419b1dabf79590a54dd3d75,2023-03-23 14:25:04.248,2023-03-23 14:25:04.248,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,22,"",https://www.teambition.com/task/6419b1dabf79590a54dd3d75,"",6419b1dabf79590a54dd3d75,fasdzvaerrw,"","",REQUIREMENT,IN_PROGRESS,测试中,,2023-03-21 13:32:10.205,2023-03-21 13:32:24.611,"",-10,0,0,0,5f27709685e4266322e2690a,coldgust,5f27709685e4266322e2690a,coldgust,"","",0, [...]
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/sprint_issues.csv b/backend/plugins/teambition/e2e/snapshot_tables/sprint_issues.csv
new file mode 100644
index 000000000..cfbf12d2a
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/sprint_issues.csv
@@ -0,0 +1,11 @@
+created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,sprint_id,issue_id
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,2,"",teambition:TeambitionSprint:1:641889b4547467946c9ad2c8,teambition:TeambitionTask:1:64132c945f3fd80070965939
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,3,"",teambition:TeambitionSprint:1:641889b4547467946c9ad2c8,teambition:TeambitionTask:1:641889e2f98ea19169bab8dd
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,10,"",teambition:TeambitionSprint:1:641889b4547467946c9ad2c8,teambition:TeambitionTask:1:6419a3d0e6a450725f9b8205
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,11,"",teambition:TeambitionSprint:1:641889b4547467946c9ad2c8,teambition:TeambitionTask:1:6419a3e15f3fd8007098bd03
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,8,"",teambition:TeambitionSprint:1:6419a3fe514a20109f89e557,teambition:TeambitionTask:1:6419a35ff98ea19169bb4a83
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,9,"",teambition:TeambitionSprint:1:6419a3fe514a20109f89e557,teambition:TeambitionTask:1:6419a3c24bccff5385d90268
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,22,"",teambition:TeambitionSprint:1:6419a3fe514a20109f89e557,teambition:TeambitionTask:1:6419b1dabf79590a54dd3d75
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,4,"",teambition:TeambitionSprint:1:6419a406fbb99df0501fef07,teambition:TeambitionTask:1:64188f3e7e30eb94d86f8792
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,7,"",teambition:TeambitionSprint:1:6419a406fbb99df0501fef07,teambition:TeambitionTask:1:6419a357bf79590a54dd3a28
+2023-03-23 14:25:04.277,2023-03-23 14:25:04.277,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_tasks,12,"",teambition:TeambitionSprint:1:6419a406fbb99df0501fef07,teambition:TeambitionTask:1:6419a466f407a6bb9c9e31ae
diff --git a/backend/plugins/teambition/e2e/snapshot_tables/sprints.csv b/backend/plugins/teambition/e2e/snapshot_tables/sprints.csv
new file mode 100644
index 000000000..4dd160793
--- /dev/null
+++ b/backend/plugins/teambition/e2e/snapshot_tables/sprints.csv
@@ -0,0 +1,4 @@
+id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,url,status,started_date,ended_date,completed_date,original_board_id
+teambition:TeambitionSprint:1:641889b4547467946c9ad2c8,2023-03-23 14:24:57.699,2023-03-23 14:24:57.699,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,1,"",beta1.0,https://www.teambition.com/project/64132c94f0d59df1c9825ab8/sprint/section/641889b4547467946c9ad2c8,future,,,,teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8
+teambition:TeambitionSprint:1:6419a3fe514a20109f89e557,2023-03-23 14:24:57.699,2023-03-23 14:24:57.699,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,2,"",beta2.0,https://www.teambition.com/project/64132c94f0d59df1c9825ab8/sprint/section/6419a3fe514a20109f89e557,future,,,,teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8
+teambition:TeambitionSprint:1:6419a406fbb99df0501fef07,2023-03-23 14:24:57.699,2023-03-23 14:24:57.699,"{""ConnectionId"":1,""OrganizationId"":"""",""ProjectId"":""64132c94f0d59df1c9825ab8""}",_raw_teambition_api_sprints,3,"",beta3.0,https://www.teambition.com/project/64132c94f0d59df1c9825ab8/sprint/section/6419a406fbb99df0501fef07,future,,,,teambition:TeambitionProject:1:64132c94f0d59df1c9825ab8
diff --git a/backend/plugins/teambition/e2e/sprint_test.go b/backend/plugins/teambition/e2e/sprint_test.go
new file mode 100644
index 000000000..2b472a4aa
--- /dev/null
+++ b/backend/plugins/teambition/e2e/sprint_test.go
@@ -0,0 +1,69 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionSprints(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_sprints.csv",
+ "_raw_teambition_api_sprints")
+ dataflowTester.FlushTabler(&models.TeambitionSprint{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractSprintsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionSprint{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_sprints.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{"created", "updated", "start_date", "end_date"},
+ },
+ )
+
+ dataflowTester.FlushTabler(&ticket.Sprint{})
+ dataflowTester.Subtask(tasks.ConvertSprintsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.Sprint{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/sprints.csv",
+ IgnoreFields: []string{"created_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/task_comment_test.go b/backend/plugins/teambition/e2e/task_comment_test.go
new file mode 100644
index 000000000..0de7de0c9
--- /dev/null
+++ b/backend/plugins/teambition/e2e/task_comment_test.go
@@ -0,0 +1,69 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionTaskComment(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_task_activities.csv",
+ "_raw_teambition_api_task_activities")
+ dataflowTester.FlushTabler(&models.TeambitionTaskActivity{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTaskActivitiesMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTaskActivity{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_task_activities.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{"created", "updated", "start_date", "end_date", "create_time", "update_time"},
+ },
+ )
+
+ dataflowTester.FlushTabler(&ticket.IssueComment{})
+ dataflowTester.Subtask(tasks.ConvertTaskCommentsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.IssueComment{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_comments.csv",
+ IgnoreFields: []string{"created_date", "logged_date", "started_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/task_scenarios_test.go b/backend/plugins/teambition/e2e/task_scenarios_test.go
new file mode 100644
index 000000000..dc852f95e
--- /dev/null
+++ b/backend/plugins/teambition/e2e/task_scenarios_test.go
@@ -0,0 +1,65 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionTaskScenarios(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_task_scenarios.csv",
+ "_raw_teambition_api_task_scenarios")
+ dataflowTester.FlushTabler(&models.TeambitionTaskScenario{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTaskScenariosMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTaskScenario{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_task_scenarios.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{
+ "created",
+ "updated",
+ "start_date",
+ "end_date",
+ "create_time",
+ "update_time",
+ "date",
+ "started_date",
+ },
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/task_tag_test.go b/backend/plugins/teambition/e2e/task_tag_test.go
new file mode 100644
index 000000000..481972b5b
--- /dev/null
+++ b/backend/plugins/teambition/e2e/task_tag_test.go
@@ -0,0 +1,74 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionTaskTags(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_task_tags.csv",
+ "_raw_teambition_api_task_tags")
+ dataflowTester.FlushTabler(&models.TeambitionTaskTag{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTaskTagsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTaskTag{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_task_tags.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{"created", "updated", "start_date", "end_date", "create_time", "update_time"},
+ },
+ )
+ dataflowTester.FlushTabler(&models.TeambitionProject{})
+ dataflowTester.FlushTabler(&models.TeambitionTaskTagTask{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_projects.csv",
+ &models.TeambitionProject{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_task_tag_tasks.csv",
+ &models.TeambitionTaskTagTask{})
+ dataflowTester.FlushTabler(&ticket.IssueLabel{})
+ dataflowTester.Subtask(tasks.ConvertTaskTagTasksMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.IssueLabel{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_labels.csv",
+ IgnoreFields: []string{"created_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/task_test.go b/backend/plugins/teambition/e2e/task_test.go
new file mode 100644
index 000000000..37f98dfb1
--- /dev/null
+++ b/backend/plugins/teambition/e2e/task_test.go
@@ -0,0 +1,100 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionTask(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_tasks.csv",
+ "_raw_teambition_api_tasks")
+ dataflowTester.FlushTabler(&models.TeambitionTask{})
+
+ dataflowTester.FlushTabler(&models.TeambitionTaskTagTask{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_task_tag_tasks.csv",
+ &models.TeambitionTaskTagTask{})
+
+ dataflowTester.FlushTabler(&models.TeambitionProject{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_projects.csv",
+ &models.TeambitionProject{})
+
+ dataflowTester.FlushTabler(&models.TeambitionAccount{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_accounts.csv",
+ &models.TeambitionAccount{})
+
+ dataflowTester.FlushTabler(&models.TeambitionTaskScenario{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_task_scenarios.csv",
+ &models.TeambitionTaskScenario{})
+
+ dataflowTester.FlushTabler(&models.TeambitionTaskFlowStatus{})
+ dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_teambition_task_flow_status.csv",
+ &models.TeambitionTaskFlowStatus{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTasksMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTask{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_tasks.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{
+ "created",
+ "updated",
+ "start_date",
+ "end_date",
+ "create_time",
+ "update_time",
+ "date",
+ "started_date",
+ "due_date",
+ "accomplish_time",
+ },
+ },
+ )
+
+ dataflowTester.FlushTabler(&ticket.Issue{})
+ dataflowTester.Subtask(tasks.ConvertTasksMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.Issue{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issues.csv",
+ IgnoreFields: []string{"created_date", "logged_date", "started_date", "updated_date", "resolution_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/task_work_flow_status_test.go b/backend/plugins/teambition/e2e/task_work_flow_status_test.go
new file mode 100644
index 000000000..b8eef64c2
--- /dev/null
+++ b/backend/plugins/teambition/e2e/task_work_flow_status_test.go
@@ -0,0 +1,65 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionTaskWorkFlowStatus(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_task_flow_status.csv",
+ "_raw_teambition_api_task_flow_status")
+ dataflowTester.FlushTabler(&models.TeambitionTaskFlowStatus{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTaskFlowStatusMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTaskFlowStatus{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_task_flow_status.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{
+ "created",
+ "updated",
+ "start_date",
+ "end_date",
+ "create_time",
+ "update_time",
+ "date",
+ "started_date",
+ },
+ },
+ )
+}
diff --git a/backend/plugins/teambition/e2e/task_worktime_test.go b/backend/plugins/teambition/e2e/task_worktime_test.go
new file mode 100644
index 000000000..d04bf771b
--- /dev/null
+++ b/backend/plugins/teambition/e2e/task_worktime_test.go
@@ -0,0 +1,78 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "testing"
+)
+
+func TestTeambitionTaskWorkTime(t *testing.T) {
+
+ var teambition impl.Teambition
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "teambition", teambition)
+
+ taskData := &tasks.TeambitionTaskData{
+ Options: &tasks.TeambitionOptions{
+ ConnectionId: 1,
+ ProjectId: "64132c94f0d59df1c9825ab8",
+ },
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_teambition_api_task_worktime.csv",
+ "_raw_teambition_api_task_worktime")
+ dataflowTester.FlushTabler(&models.TeambitionTaskWorktime{})
+
+ // verify extraction
+ dataflowTester.Subtask(tasks.ExtractTaskWorktimeMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ models.TeambitionTaskWorktime{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_teambition_task_worktime.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ IgnoreFields: []string{
+ "created",
+ "updated",
+ "start_date",
+ "end_date",
+ "create_time",
+ "update_time",
+ "date",
+ "started_date",
+ },
+ },
+ )
+
+ dataflowTester.FlushTabler(&ticket.IssueWorklog{})
+ dataflowTester.Subtask(tasks.ConvertTaskWorktimeMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ ticket.IssueWorklog{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_worklogs.csv",
+ IgnoreFields: []string{"created_date", "logged_date", "started_date"},
+ IgnoreTypes: []interface{}{domainlayer.DomainEntity{}},
+ },
+ )
+}
diff --git a/backend/plugins/teambition/impl/impl.go b/backend/plugins/teambition/impl/impl.go
new file mode 100644
index 000000000..dcc5242ca
--- /dev/null
+++ b/backend/plugins/teambition/impl/impl.go
@@ -0,0 +1,171 @@
+/*
+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 impl
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/dal"
+ "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/plugins/teambition/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "github.com/apache/incubator-devlake/plugins/teambition/models/migrationscripts"
+ "github.com/apache/incubator-devlake/plugins/teambition/tasks"
+ "time"
+)
+
+// make sure interface is implemented
+var _ plugin.PluginMeta = (*Teambition)(nil)
+var _ plugin.PluginInit = (*Teambition)(nil)
+var _ plugin.PluginTask = (*Teambition)(nil)
+var _ plugin.PluginApi = (*Teambition)(nil)
+var _ plugin.CloseablePluginTask = (*Teambition)(nil)
+
+type Teambition struct{}
+
+func (p Teambition) Description() string {
+ return "collect some Teambition data"
+}
+
+func (p Teambition) Init(br context.BasicRes) errors.Error {
+ api.Init(br)
+ return nil
+}
+
+func (p Teambition) GetTablesInfo() []dal.Tabler {
+ return []dal.Tabler{
+ &models.TeambitionConnection{},
+ &models.TeambitionAccount{},
+ &models.TeambitionTask{},
+ &models.TeambitionTaskTagTask{},
+ &models.TeambitionTaskTag{},
+ &models.TeambitionSprint{},
+ &models.TeambitionTaskActivity{},
+ &models.TeambitionTaskWorktime{},
+ &models.TeambitionProject{},
+ }
+}
+
+func (p Teambition) SubTaskMetas() []plugin.SubTaskMeta {
+ return []plugin.SubTaskMeta{
+ tasks.CollectAccountsMeta,
+ tasks.ExtractAccountsMeta,
+ tasks.ConvertAccountsMeta,
+ tasks.CollectTasksMeta,
+ tasks.ExtractTasksMeta,
+ tasks.CollectTaskTagsMeta,
+ tasks.ExtractTaskTagsMeta,
+ tasks.ConvertTaskTagTasksMeta,
+ tasks.CollectTaskActivitiesMeta,
+ tasks.ExtractTaskActivitiesMeta,
+ tasks.ConvertTaskCommentsMeta,
+ tasks.ConvertTaskChangelogMeta,
+ tasks.CollectTaskWorktimeMeta,
+ tasks.ExtractTaskWorktimeMeta,
+ tasks.ConvertTaskWorktimeMeta,
+ tasks.CollectProjectsMeta,
+ tasks.ExtractProjectsMeta,
+ tasks.ConvertProjectsMeta,
+ tasks.CollectSprintsMeta,
+ tasks.ExtractSprintsMeta,
+ tasks.ConvertSprintsMeta,
+ tasks.CollectTaskFlowStatusMeta,
+ tasks.ExtractTaskFlowStatusMeta,
+ tasks.CollectTaskScenariosMeta,
+ tasks.ExtractTaskScenariosMeta,
+ tasks.ConvertTasksMeta,
+ }
+}
+
+func (p Teambition) MakeDataSourcePipelinePlanV200(connectionId uint64, scopes []*plugin.BlueprintScopeV200, syncPolicy plugin.BlueprintSyncPolicy) (pp plugin.PipelinePlan, sc []plugin.Scope, err errors.Error) {
+ return api.MakeDataSourcePipelinePlanV200(p.SubTaskMetas(), connectionId, scopes, &syncPolicy)
+}
+
+func (p Teambition) PrepareTaskData(taskCtx plugin.TaskContext, options map[string]interface{}) (interface{}, errors.Error) {
+ op, err := tasks.DecodeAndValidateTaskOptions(options)
+ if err != nil {
+ return nil, err
+ }
+ connectionHelper := helper.NewConnectionHelper(
+ taskCtx,
+ nil,
+ )
+ connection := &models.TeambitionConnection{}
+ err = connectionHelper.FirstById(connection, op.ConnectionId)
+ if err != nil {
+ return nil, errors.Default.Wrap(err, "unable to get Teambition connection by the given connection ID")
+ }
+
+ apiClient, err := tasks.NewTeambitionApiClient(taskCtx, connection)
+ if err != nil {
+ return nil, errors.Default.Wrap(err, "unable to get Teambition API client instance")
+ }
+ taskData := &tasks.TeambitionTaskData{
+ Options: op,
+ ApiClient: apiClient,
+ TenantId: connection.TenantId,
+ }
+ var createdDateAfter time.Time
+ if op.TimeAfter != "" {
+ createdDateAfter, err = errors.Convert01(time.Parse(time.RFC3339, op.TimeAfter))
+ if err != nil {
+ return nil, errors.BadInput.Wrap(err, "invalid value for `createdDateAfter`")
+ }
+ }
+ if !createdDateAfter.IsZero() {
+ taskData.TimeAfter = &createdDateAfter
+ }
+ return taskData, nil
+}
+
+// RootPkgPath PkgPath information lost when compiled as plugin(.so)
+func (p Teambition) RootPkgPath() string {
+ return "github.com/apache/incubator-devlake/plugins/teambition"
+}
+
+func (p Teambition) MigrationScripts() []plugin.MigrationScript {
+ return migrationscripts.All()
+}
+
+func (p Teambition) ApiResources() map[string]map[string]plugin.ApiResourceHandler {
+ return map[string]map[string]plugin.ApiResourceHandler{
+ "test": {
+ "POST": api.TestConnection,
+ },
+ "connections": {
+ "POST": api.PostConnections,
+ "GET": api.ListConnections,
+ },
+ "connections/:connectionId": {
+ "GET": api.GetConnection,
+ "PATCH": api.PatchConnection,
+ "DELETE": api.DeleteConnection,
+ },
+ }
+}
+
+func (p Teambition) Close(taskCtx plugin.TaskContext) errors.Error {
+ data, ok := taskCtx.GetData().(*tasks.TeambitionTaskData)
+ if !ok {
+ return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
+ }
+ data.ApiClient.Release()
+ return nil
+}
diff --git a/backend/plugins/teambition/models/account.go b/backend/plugins/teambition/models/account.go
new file mode 100644
index 000000000..74c54bede
--- /dev/null
+++ b/backend/plugins/teambition/models/account.go
@@ -0,0 +1,51 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionAccount struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ UserId string `gorm:"primaryKey;type:varchar(100)" json:"userId"`
+ MemberId string `gorm:"type:varchar(100)" json:"memberId"`
+ IsDisabled int `json:"isDisabled"`
+ Role uint64 `json:"role"`
+ AvatarUrl string `gorm:"type:varchar(255)" json:"avatarUrl"`
+ Birthday string `gorm:"type:varchar(100)" json:"birthday"`
+ City string `gorm:"type:varchar(100)" json:"city"`
+ Province string `gorm:"type:varchar(100)" json:"province"`
+ Country string `gorm:"type:varchar(100)" json:"country"`
+ Email string `gorm:"type:varchar(255)" json:"email"`
+ EntryTime *api.Iso8601Time `json:"entryTime"`
+ Name string `gorm:"index;type:varchar(255)" json:"name"`
+ Phone string `gorm:"type:varchar(100)" json:"phone"`
+ Title string `gorm:"type:varchar(255)" json:"title"`
+ Pinyin string `gorm:"type:varchar(255)" json:"pinyin"`
+ Py string `gorm:"type:varchar(255)" json:"py"`
+ StaffType string `gorm:"type:varchar(255)" json:"staffType"`
+ EmployeeNumber string `gorm:"type:varchar(100)" json:"employeeNumber"`
+
+ common.NoPKModel
+}
+
+func (TeambitionAccount) TableName() string {
+ return "_tool_teambition_accounts"
+}
diff --git a/backend/plugins/teambition/models/connection.go b/backend/plugins/teambition/models/connection.go
new file mode 100644
index 000000000..b50d4d13b
--- /dev/null
+++ b/backend/plugins/teambition/models/connection.go
@@ -0,0 +1,62 @@
+/*
+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 models
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/golang-jwt/jwt/v5"
+ "net/http"
+ "time"
+)
+
+// TeambitionConn holds the essential information to connect to the TeambitionConn API
+type TeambitionConn struct {
+ helper.RestConnection `mapstructure:",squash"`
+ helper.AppKey `mapstructure:",squash"`
+ TenantId string `mapstructure:"tenantId" validate:"required" json:"tenantId"`
+ TenantType string `mapstructure:"tenantType" validate:"required" json:"tenantType"`
+}
+
+// TeambitionConnection holds TeambitionConn plus ID/Name for database storage
+type TeambitionConnection struct {
+ helper.BaseConnection `mapstructure:",squash"`
+ TeambitionConn `mapstructure:",squash"`
+}
+
+func (tc *TeambitionConn) SetupAuthentication(req *http.Request) errors.Error {
+ token := jwt.New(jwt.SigningMethodHS256)
+ claims := make(jwt.MapClaims)
+ claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
+ claims["iat"] = time.Now().Unix()
+ claims["_appId"] = tc.AppId
+ token.Claims = claims
+ tokenString, err := token.SignedString([]byte(tc.SecretKey))
+ if err != err {
+ return errors.Convert(err)
+ }
+ req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", tokenString))
+ req.Header.Set("X-Tenant-Id", tc.TenantId)
+ req.Header.Set("X-Tenant-Type", tc.TenantType)
+ return nil
+}
+
+func (TeambitionConnection) TableName() string {
+ return "_tool_teambition_connections"
+}
diff --git a/backend/plugins/teambition/models/input.go b/backend/plugins/teambition/models/input.go
new file mode 100644
index 000000000..84bc7f78a
--- /dev/null
+++ b/backend/plugins/teambition/models/input.go
@@ -0,0 +1,27 @@
+/*
+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 models
+
+import (
+ "time"
+)
+
+type Input struct {
+ TaskId string `json:"taskId"`
+ Updated *time.Time `json:"updated"`
+}
diff --git a/backend/plugins/teambition/models/migrationscripts/20230314_add_init_tables.go b/backend/plugins/teambition/models/migrationscripts/20230314_add_init_tables.go
new file mode 100644
index 000000000..07a1ea527
--- /dev/null
+++ b/backend/plugins/teambition/models/migrationscripts/20230314_add_init_tables.go
@@ -0,0 +1,52 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/helpers/migrationhelper"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+)
+
+type addInitTables struct{}
+
+func (*addInitTables) Up(basicRes context.BasicRes) errors.Error {
+ return migrationhelper.AutoMigrateTables(
+ basicRes,
+ &models.TeambitionConnection{},
+ &models.TeambitionAccount{},
+ &models.TeambitionTask{},
+ &models.TeambitionTaskActivity{},
+ &models.TeambitionSprint{},
+ &models.TeambitionTaskTag{},
+ &models.TeambitionTaskTagTask{},
+ &models.TeambitionTaskWorktime{},
+ &models.TeambitionProject{},
+ &models.TeambitionTaskFlowStatus{},
+ &models.TeambitionTaskScenario{},
+ )
+}
+
+func (*addInitTables) Version() uint64 {
+ return 20230314000001
+}
+
+func (*addInitTables) Name() string {
+ return "teambition init schemas"
+}
diff --git a/backend/plugins/teambition/models/migrationscripts/register.go b/backend/plugins/teambition/models/migrationscripts/register.go
new file mode 100644
index 000000000..a26815ea5
--- /dev/null
+++ b/backend/plugins/teambition/models/migrationscripts/register.go
@@ -0,0 +1,27 @@
+/*
+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 "github.com/apache/incubator-devlake/core/plugin"
+
+// All return all the migration scripts
+func All() []plugin.MigrationScript {
+ return []plugin.MigrationScript{
+ new(addInitTables),
+ }
+}
diff --git a/backend/plugins/teambition/models/project.go b/backend/plugins/teambition/models/project.go
new file mode 100644
index 000000000..6f00a9d5e
--- /dev/null
+++ b/backend/plugins/teambition/models/project.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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionProject struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ Name string `gorm:"type:varchar(255)" json:"name"`
+ Logo string `gorm:"type:varchar(255)" json:"logo"`
+ Description string `gorm:"type:text" json:"description"`
+ OrganizationId string `gorm:"type:varchar(255)" json:"organizationId"`
+ Visibility string `gorm:"typechar(100)" json:"visibility"`
+ IsTemplate bool `json:"isTemplate"`
+ CreatorId string `gorm:"type:varchar(100)" json:"creatorId"`
+ IsArchived bool `json:"isArchived"`
+ IsSuspended bool `json:"isSuspended"`
+ UniqueIdPrefix string `gorm:"type:varchar(255)" json:"uniqueIdPrefix"`
+ Created *api.Iso8601Time `json:"created"`
+ Updated *api.Iso8601Time `json:"updated"`
+ StartDate *api.Iso8601Time `json:"startDate"`
+ EndDate *api.Iso8601Time `json:"endDate"`
+ Customfields []TeambitionProjectCustomField `gorm:"serializer:json;type:text" json:"customfields"`
+
+ common.NoPKModel
+}
+
+type TeambitionProjectCustomField struct {
+ CustomfieldId string `json:"customfieldId"`
+ Type string `json:"type"`
+ Value []TeambitionCustomFieldValue `json:"value"`
+}
+
+type TeambitionProjectCustomFieldValue struct {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ MetaString string `json:"metaString"`
+}
+
+func (TeambitionProject) TableName() string {
+ return "_tool_teambition_projects"
+}
diff --git a/backend/plugins/teambition/models/sprint.go b/backend/plugins/teambition/models/sprint.go
new file mode 100644
index 000000000..c2c8692f6
--- /dev/null
+++ b/backend/plugins/teambition/models/sprint.go
@@ -0,0 +1,47 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionSprint struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ Name string `gorm:"type:varchar(255)" json:"name"`
+ ExecutorId string `gorm:"type:varchar(100)" json:"executorId"`
+ Description string `gorm:"type:text" json:"description"`
+ Status string `gorm:"varchar(255)" json:"status"`
+ ProjectId string `gorm:"type:varchar(100)" json:"projectId"`
+ CreatorId string `gorm:"type:varchar(100)" json:"creatorId"`
+ StartDate *api.Iso8601Time `json:"startDate"`
+ DueDate *api.Iso8601Time `json:"dueDate"`
+ Accomplished *api.Iso8601Time `json:"accomplished"`
+ Created *api.Iso8601Time `json:"created"`
+ Updated *api.Iso8601Time `json:"updated"`
+ Payload any `gorm:"serializer:json;type:text" json:"payload"`
+ Labels []string `gorm:"serializer:json;type:text" json:"labels"`
+
+ common.NoPKModel
+}
+
+func (TeambitionSprint) TableName() string {
+ return "_tool_teambition_sprints"
+}
diff --git a/backend/plugins/teambition/models/task.go b/backend/plugins/teambition/models/task.go
new file mode 100644
index 000000000..a748a2a76
--- /dev/null
+++ b/backend/plugins/teambition/models/task.go
@@ -0,0 +1,75 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionTask struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ ProjectId string `gorm:"primaryKey;type:varchar(100)" json:"projectId"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ Content string `gorm:"type:varchar(255)" json:"content"`
+ Note string `gorm:"type:varchar(255)" json:"Content"`
+ AncestorIds []string `gorm:"serializer:json;type:text" json:"ancestorIds"`
+ ParentTaskId string `gorm:"type:varchar(100)" json:"parentTaskId"`
+ TfsId string `gorm:"type:varchar(100)" json:"tfsId"`
+ TasklistId string `gorm:"type:varchar(100)" json:"tasklistId"`
+ StageId string `gorm:"type:varchar(100)" json:"stageId"`
+ TagIds []string `gorm:"serializer:json;type:text" json:"tagIds"`
+ CreatorId string `gorm:"type:varchar(100)" json:"creatorId"`
+ ExecutorId string `gorm:"type:varchar(100)" json:"executorId"`
+ InvolveMembers []string `gorm:"serializer:json;type:text" json:"involveMembers"`
+ Priority int `json:"priority"`
+ StoryPoint string `gorm:"varchar(255)" json:"storyPoint"`
+ Recurrence []string `gorm:"serializer:json;type:text" json:"recurrence"`
+ IsDone bool `json:"isDone"`
+ IsArchived bool `json:"isArchived"`
+ Visible string `gorm:"varchar(100)" json:"visible"`
+ UniqueId int64 `json:"uniqueId"`
+ StartDate *api.Iso8601Time `json:"startDate"`
+ DueDate *api.Iso8601Time `json:"dueDate"`
+ AccomplishTime *api.Iso8601Time `json:"accomplishTime"`
+ Created *api.Iso8601Time `json:"created"`
+ Updated *api.Iso8601Time `json:"updated"`
+ SfcId string `gorm:"type:varchar(100)" json:"sfcId"`
+ SprintId string `gorm:"type:varchar(100)" json:"sprintId"`
+ Customfields []TeambitionCustomField `gorm:"serializer:json;type:text" json:"customfields"`
+
+ StdType string `gorm:"type:varchar(100)" json:"stdType"`
+ StdStatus string `gorm:"type:varchar(100)" json:"stdStatus"`
+
+ common.NoPKModel
+}
+
+type TeambitionCustomField struct {
+ CfId string `gorm:"varchar(100)" json:"cfId"`
+ Type string `gorm:"varchar(100)" json:"type"`
+}
+
+type TeambitionCustomFieldValue struct {
+ Id string `gorm:"varchar(100)" json:"id"`
+ Title string `gorm:"varchar(100)" json:"title"`
+ MetaString string `gorm:"varchar(100)" json:"metaString"`
+}
+
+func (TeambitionTask) TableName() string {
+ return "_tool_teambition_tasks"
+}
diff --git a/backend/plugins/teambition/models/task_activity.go b/backend/plugins/teambition/models/task_activity.go
new file mode 100644
index 000000000..947189e9a
--- /dev/null
+++ b/backend/plugins/teambition/models/task_activity.go
@@ -0,0 +1,43 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionTaskActivity struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ ProjectId string `gorm:"primaryKey;type:varchar(100)" json:"projectId"`
+ TaskId string `gorm:"primaryKey;type:varchar(100)" json:"taskId"`
+ CreatorId string `gorm:"type:varchar(100)" json:"creatorId"`
+ Action string `gorm:"type:varchar(100)" json:"action"`
+ BoundToObjectId string `gorm:"type:varchar(100)" json:"boundToObjectId"`
+ BoundToObjectType string `gorm:"type:varchar(100)" json:"BoundToObjectType"`
+ CreateTime *api.Iso8601Time `json:"createTime"`
+ UpdateTime *api.Iso8601Time `json:"updateTime"`
+ Content string `gorm:"type:text" json:"content"`
+
+ common.NoPKModel
+}
+
+func (TeambitionTaskActivity) TableName() string {
+ return "_tool_teambition_task_activities"
+}
diff --git a/backend/plugins/teambition/models/task_comment.go b/backend/plugins/teambition/models/task_comment.go
new file mode 100644
index 000000000..ae23b7aee
--- /dev/null
+++ b/backend/plugins/teambition/models/task_comment.go
@@ -0,0 +1,27 @@
+/*
+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 models
+
+type TeambitionTaskComment struct {
+ Comment string `json:"comment"`
+ IsOnlyNotifyMentions bool `json:"isOnlyNotifyMentions"`
+ IsDingtalkPM bool `json:"isDingtalkPM"`
+ RenderMode string `json:"renderMode"`
+ Attachments []string `json:"attachments"`
+ DingFiles []string `json:"dingFiles"`
+}
diff --git a/backend/plugins/teambition/models/task_flow_status.go b/backend/plugins/teambition/models/task_flow_status.go
new file mode 100644
index 000000000..4f8578bd4
--- /dev/null
+++ b/backend/plugins/teambition/models/task_flow_status.go
@@ -0,0 +1,45 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionTaskFlowStatus struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ ProjectId string `gorm:"primaryKey;type:varchar(100)" json:"projectId"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ Name string `gorm:"type:varchar(100)" json:"name"`
+ Pos int64 `json:"pos"`
+ TaskflowId string `gorm:"type:varchar(100)" json:"taskflowId"`
+ RejectStatusIds []string `gorm:"serializer:json;type:text" json:"rejectStatusIds"`
+ Kind string `gorm:"varchar(100)" json:"kind"`
+ CreatorId string `gorm:"varchar(100)" json:"creatorId"`
+ IsDeleted bool `json:"isDeleted"`
+ IsTaskflowstatusruleexector bool `json:"isTaskflowstatusruleexector"`
+ Created *api.Iso8601Time `json:"created"`
+ Updated *api.Iso8601Time `json:"updated"`
+
+ common.NoPKModel
+}
+
+func (TeambitionTaskFlowStatus) TableName() string {
+ return "_tool_teambition_task_flow_status"
+}
diff --git a/backend/plugins/teambition/models/task_scenario.go b/backend/plugins/teambition/models/task_scenario.go
new file mode 100644
index 000000000..3f52bb60e
--- /dev/null
+++ b/backend/plugins/teambition/models/task_scenario.go
@@ -0,0 +1,53 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionTaskScenario struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ ProjectId string `gorm:"primaryKey;type:varchar(100)" json:"projectId"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ Name string `gorm:"type:varchar(100)" json:"name"`
+ Icon string `gorm:"type:varchar(100)" json:"icon"`
+ Type string `gorm:"type:varchar(100)" json:"type"`
+ Scenariofields []TeambitionScenarioField `gorm:"serializer:json;type:text" json:"scenariofields"`
+ CreatorId string `gorm:"type:varchar(100)" json:"creatorId"`
+ Source string `gorm:"type:varchar(100)" json:"source"`
+ BoundToObjectId string `gorm:"type:varchar(100)" json:"boundToObjectId"`
+ BoundToObjectType string `gorm:"type:varchar(100)" json:"boundToObjectType"`
+ TaskflowId string `gorm:"type:varchar(100)" json:"taskflowId"`
+ IsArchived bool `json:"isArchived"`
+ Created *api.Iso8601Time `json:"created"`
+ Updated *api.Iso8601Time `json:"updated"`
+
+ common.NoPKModel
+}
+
+type TeambitionScenarioField struct {
+ CustomfieldId string `json:"customfieldId"`
+ FieldType string `json:"fieldType"`
+ Required bool `json:"required"`
+}
+
+func (TeambitionTaskScenario) TableName() string {
+ return "_tool_teambition_task_scenarios"
+}
diff --git a/backend/plugins/teambition/models/task_tag.go b/backend/plugins/teambition/models/task_tag.go
new file mode 100644
index 000000000..40947892a
--- /dev/null
+++ b/backend/plugins/teambition/models/task_tag.go
@@ -0,0 +1,42 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionTaskTag struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ Id string `gorm:"primaryKey;type:varchar(100)" json:"id"`
+ CreatorId string `gorm:"type:varchar(100)" json:"creatorId"`
+ ProjectId string `gorm:"type:varchar(100)" json:"projectId"`
+ OrganizationId string `gorm:"type:varchar(100)" json:"organizationId"`
+ Name string `gorm:"type:varchar(100)" json:"name"`
+ Color string `gorm:"type:varchar(100)" json:"color"`
+ IsArchived bool `json:"isArchived"`
+ Created *api.Iso8601Time `json:"created"`
+ Updated *api.Iso8601Time `json:"updated"`
+
+ common.NoPKModel
+}
+
+func (TeambitionTaskTag) TableName() string {
+ return "_tool_teambition_task_tags"
+}
diff --git a/backend/plugins/teambition/models/task_tag_task.go b/backend/plugins/teambition/models/task_tag_task.go
new file mode 100644
index 000000000..db2e93ead
--- /dev/null
+++ b/backend/plugins/teambition/models/task_tag_task.go
@@ -0,0 +1,34 @@
+/*
+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 models
+
+import "github.com/apache/incubator-devlake/core/models/common"
+
+type TeambitionTaskTagTask struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ ProjectId string `gorm:"primaryKey;type:varchar(100)"`
+ TaskId string `gorm:"primaryKey;type:varchar(100)"`
+ TaskTagId string `gorm:"primaryKey;type:varchar(100)"`
+ Name string `gorm:"type:varchar(100)" json:"name"`
+
+ common.NoPKModel
+}
+
+func (TeambitionTaskTagTask) TableName() string {
+ return "_tool_teambition_task_tag_tasks"
+}
diff --git a/backend/plugins/teambition/models/task_worktime.go b/backend/plugins/teambition/models/task_worktime.go
new file mode 100644
index 000000000..dd7225033
--- /dev/null
+++ b/backend/plugins/teambition/models/task_worktime.go
@@ -0,0 +1,46 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+)
+
+type TeambitionTaskWorktime struct {
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT"`
+ ProjectId string `gorm:"primaryKey;type:varchar(100)" json:"projectId"`
+ TaskId string `gorm:"primaryKey;type:varchar(100)" json:"taskId"`
+ WorktimeId string `gorm:"primaryKey;type:varchar(100)" json:"worktimeId"`
+ ObjectType string `gorm:"type:varchar(100)" json:"objectType"`
+ ObjectId string `gorm:"type:varchar(100)" json:"objectId"`
+ Worktime uint64 `json:"worktime"`
+ UserId string `gorm:"type:varchar(100)" json:"userId"`
+ Date *api.Iso8601Time `json:"date"`
+ Description string `gorm:"type:text" json:"description"`
+ OrgId string `gorm:"type:varchar(100)" json:"orgId"`
+ SubmitterId string `gorm:"type:varchar(100)" json:"submitterId"`
+ CreatedAt *api.Iso8601Time `json:"createdAt"`
+ UpdatedAt *api.Iso8601Time `json:"updatedAt"`
+
+ common.NoPKModel
+}
+
+func (TeambitionTaskWorktime) TableName() string {
+ return "_tool_teambition_task_worktime"
+}
diff --git a/backend/plugins/teambition/tasks/account_collector.go b/backend/plugins/teambition/tasks/account_collector.go
new file mode 100644
index 000000000..db916bd2c
--- /dev/null
+++ b/backend/plugins/teambition/tasks/account_collector.go
@@ -0,0 +1,65 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_USER_TABLE = "teambition_api_users"
+
+var _ plugin.SubTaskEntryPoint = CollectAccounts
+
+var CollectAccountsMeta = plugin.SubTaskMeta{
+ Name: "collectAccounts",
+ EntryPoint: CollectAccounts,
+ EnabledByDefault: true,
+ Description: "collect teambition accounts",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS},
+}
+
+func CollectAccounts(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_USER_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect accounts")
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ UrlTemplate: "/org/member/list",
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ query.Set("orgId", data.TenantId)
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ data := TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect user error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/account_converter.go b/backend/plugins/teambition/tasks/account_converter.go
new file mode 100644
index 000000000..f266244db
--- /dev/null
+++ b/backend/plugins/teambition/tasks/account_converter.go
@@ -0,0 +1,83 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertAccountsMeta = plugin.SubTaskMeta{
+ Name: "convertAccounts",
+ EntryPoint: ConvertAccounts,
+ EnabledByDefault: true,
+ Description: "convert teambition account",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS},
+}
+
+func ConvertAccounts(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_USER_TABLE)
+ db := taskCtx.GetDal()
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionAccount{}),
+ dal.Where("connection_id = ?", data.Options.ConnectionId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionAccount{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionAccount)
+ account := &crossdomain.Account{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getAccountIdGen().Generate(data.Options.ConnectionId, userTool.UserId),
+ },
+ UserName: userTool.Name,
+ FullName: userTool.Name,
+ Email: userTool.Email,
+ AvatarUrl: userTool.AvatarUrl,
+ Status: userTool.IsDisabled,
+ }
+ if userTool.EntryTime != nil {
+ account.CreatedDate = userTool.EntryTime.ToNullableTime()
+ }
+
+ return []interface{}{
+ account,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/account_extractor.go b/backend/plugins/teambition/tasks/account_extractor.go
new file mode 100644
index 000000000..7522dc2ff
--- /dev/null
+++ b/backend/plugins/teambition/tasks/account_extractor.go
@@ -0,0 +1,66 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractAccounts
+
+var ExtractAccountsMeta = plugin.SubTaskMeta{
+ Name: "extractAccounts",
+ EntryPoint: ExtractAccounts,
+ EnabledByDefault: true,
+ Description: "Extract raw workspace data into tool layer table _tool_teambition_accounts",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS},
+}
+
+func ExtractAccounts(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_USER_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionAccount{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ toolL := models.TeambitionAccount{
+ ConnectionId: data.Options.ConnectionId,
+ UserId: userRes.UserId,
+ Name: userRes.Name,
+ Email: userRes.Email,
+ AvatarUrl: userRes.AvatarUrl,
+ EntryTime: userRes.EntryTime,
+ }
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/api_client.go b/backend/plugins/teambition/tasks/api_client.go
new file mode 100644
index 000000000..82d9ed5b5
--- /dev/null
+++ b/backend/plugins/teambition/tasks/api_client.go
@@ -0,0 +1,48 @@
+/*
+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 tasks
+
+import (
+ "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/teambition/models"
+)
+
+func NewTeambitionApiClient(taskCtx plugin.TaskContext, connection *models.TeambitionConnection) (*api.ApiAsyncClient, errors.Error) {
+ // create synchronize api client so we can calculate api rate limit dynamically
+ apiClient, err := api.NewApiClientFromConnection(taskCtx.GetContext(), taskCtx, connection)
+ if err != nil {
+ return nil, err
+ }
+
+ // create rate limit calculator
+ rateLimiter := &api.ApiRateLimitCalculator{
+ UserRateLimitPerHour: connection.RateLimitPerHour,
+ }
+ asyncApiClient, err := api.CreateAsyncApiClient(
+ taskCtx,
+ apiClient,
+ rateLimiter,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return asyncApiClient, nil
+}
diff --git a/backend/plugins/teambition/tasks/project_collector.go b/backend/plugins/teambition/tasks/project_collector.go
new file mode 100644
index 000000000..2ea1ffc94
--- /dev/null
+++ b/backend/plugins/teambition/tasks/project_collector.go
@@ -0,0 +1,85 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_PROJECT_TABLE = "teambition_api_projects"
+
+var _ plugin.SubTaskEntryPoint = CollectProjects
+
+var CollectProjectsMeta = plugin.SubTaskMeta{
+ Name: "collectProjects",
+ EntryPoint: CollectProjects,
+ EnabledByDefault: true,
+ Description: "collect teambition projects",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectProjects(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PROJECT_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect projects")
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ UrlTemplate: "/v3/project/query",
+ PageSize: int(data.Options.PageSize),
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ query.Set("projectIds", data.Options.ProjectId)
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ data := TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect projects error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/project_convertor.go b/backend/plugins/teambition/tasks/project_convertor.go
new file mode 100644
index 000000000..53d6fe94c
--- /dev/null
+++ b/backend/plugins/teambition/tasks/project_convertor.go
@@ -0,0 +1,80 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertProjectsMeta = plugin.SubTaskMeta{
+ Name: "convertAccounts",
+ EntryPoint: ConvertProjects,
+ EnabledByDefault: true,
+ Description: "convert teambition projects",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertProjects(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PROJECT_TABLE)
+ db := taskCtx.GetDal()
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionProject{}),
+ dal.Where("connection_id = ?", data.Options.ConnectionId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionProject{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionProject)
+ account := &ticket.Board{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getProjectIdGen().Generate(data.Options.ConnectionId, userTool.Id),
+ },
+ Name: userTool.Name,
+ Description: userTool.Description,
+ Url: fmt.Sprintf("https://www.teambition.com/project/%s", userTool.Id),
+ CreatedDate: userTool.Created.ToNullableTime(),
+ }
+
+ return []interface{}{
+ account,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/project_extractor.go b/backend/plugins/teambition/tasks/project_extractor.go
new file mode 100644
index 000000000..d850fb90f
--- /dev/null
+++ b/backend/plugins/teambition/tasks/project_extractor.go
@@ -0,0 +1,60 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractProjects
+
+var ExtractProjectsMeta = plugin.SubTaskMeta{
+ Name: "extractProjects",
+ EntryPoint: ExtractProjects,
+ EnabledByDefault: true,
+ Description: "Extract raw data into tool layer table _tool_teambition_projects",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractProjects(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PROJECT_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionProject{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ toolL := userRes
+ toolL.ConnectionId = data.Options.ConnectionId
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/shared.go b/backend/plugins/teambition/tasks/shared.go
new file mode 100644
index 000000000..ce2b40129
--- /dev/null
+++ b/backend/plugins/teambition/tasks/shared.go
@@ -0,0 +1,172 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "strings"
+)
+
+type TeambitionApiParams struct {
+ ConnectionId uint64
+ OrganizationId string
+ ProjectId string
+}
+
+type TeambitionComRes[T any] struct {
+ NextPageToken string `json:"nextPageToken"`
+ TotalSize int `json:"totalSize"`
+ Result T `json:"result"`
+ Code int `json:"code"`
+ ErrorMessage string `json:"errorMessage"`
+ RequestId string `json:"requestId"`
+}
+
+var accountIdGen *didgen.DomainIdGenerator
+var taskIdGen *didgen.DomainIdGenerator
+var taskActivityIdGen *didgen.DomainIdGenerator
+var taskWorktimeIdGen *didgen.DomainIdGenerator
+var projectIdGen *didgen.DomainIdGenerator
+var sprintIdGen *didgen.DomainIdGenerator
+
+func getAccountIdGen() *didgen.DomainIdGenerator {
+ if accountIdGen == nil {
+ accountIdGen = didgen.NewDomainIdGenerator(&models.TeambitionAccount{})
+ }
+ return accountIdGen
+}
+
+func getTaskIdGen() *didgen.DomainIdGenerator {
+ if taskIdGen == nil {
+ taskIdGen = didgen.NewDomainIdGenerator(&models.TeambitionTask{})
+ }
+ return taskIdGen
+}
+
+func getTaskActivityIdGen() *didgen.DomainIdGenerator {
+ if taskActivityIdGen == nil {
+ taskActivityIdGen = didgen.NewDomainIdGenerator(&models.TeambitionTaskActivity{})
+ }
+ return taskActivityIdGen
+}
+
+func getProjectIdGen() *didgen.DomainIdGenerator {
+ if projectIdGen == nil {
+ projectIdGen = didgen.NewDomainIdGenerator(&models.TeambitionProject{})
+ }
+ return projectIdGen
+}
+
+func getTaskWorktimeIdGen() *didgen.DomainIdGenerator {
+ if taskWorktimeIdGen == nil {
+ taskWorktimeIdGen = didgen.NewDomainIdGenerator(&models.TeambitionTaskWorktime{})
+ }
+ return taskWorktimeIdGen
+}
+
+func getSprintIdGen() *didgen.DomainIdGenerator {
+ if sprintIdGen == nil {
+ sprintIdGen = didgen.NewDomainIdGenerator(&models.TeambitionSprint{})
+ }
+ return sprintIdGen
+}
+
+func CreateRawDataSubTaskArgs(taskCtx plugin.SubTaskContext, rawTable string) (*api.RawDataSubTaskArgs, *TeambitionTaskData) {
+ data := taskCtx.GetData().(*TeambitionTaskData)
+ filteredData := *data
+ filteredData.Options = &TeambitionOptions{}
+ *filteredData.Options = *data.Options
+ params := TeambitionApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProjectId: data.Options.ProjectId,
+ }
+ rawDataSubTaskArgs := &api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: params,
+ Table: rawTable,
+ }
+ return rawDataSubTaskArgs, &filteredData
+}
+
+func getStdTypeMappings(data *TeambitionTaskData) map[string]string {
+ stdTypeMappings := make(map[string]string)
+ for userType, stdType := range data.Options.TransformationRules.TypeMappings {
+ stdTypeMappings[userType] = strings.ToUpper(stdType.StandardType)
+ }
+ return stdTypeMappings
+}
+
+func getStatusMapping(data *TeambitionTaskData) map[string]string {
+ statusMapping := make(map[string]string)
+ mapping := data.Options.TransformationRules.StatusMappings
+ for std, orig := range mapping {
+ for _, v := range orig {
+ statusMapping[v] = std
+ }
+ }
+ return statusMapping
+}
+
+func FindAccountById(db dal.Dal, accountId string) (*models.TeambitionAccount, errors.Error) {
+ if accountId == "" {
+ return nil, errors.Default.New("account id must not empty")
+ }
+ account := &models.TeambitionAccount{}
+ if err := db.First(account, dal.Where("user_id = ?", accountId)); err != nil {
+ return nil, err
+ }
+ return account, nil
+}
+
+func FindProjectById(db dal.Dal, projectId string) (*models.TeambitionProject, errors.Error) {
+ if projectId == "" {
+ return nil, errors.Default.New("project id must not empty")
+ }
+ project := &models.TeambitionProject{}
+ if err := db.First(project, dal.Where("id = ?", projectId)); err != nil {
+ return nil, err
+ }
+ return project, nil
+}
+
+func FindTaskScenarioById(db dal.Dal, scenarioId string) (*models.TeambitionTaskScenario, errors.Error) {
+ if scenarioId == "" {
+ return nil, errors.Default.New("id must not empty")
+ }
+ scenario := &models.TeambitionTaskScenario{}
+ if err := db.First(scenario, dal.Where("id = ?", scenarioId)); err != nil {
+ return nil, err
+ }
+ return scenario, nil
+}
+
+func FindTaskFlowStatusById(db dal.Dal, id string) (*models.TeambitionTaskFlowStatus, errors.Error) {
+ if id == "" {
+ return nil, errors.Default.New("id must not empty")
+ }
+ status := &models.TeambitionTaskFlowStatus{}
+ if err := db.First(status, dal.Where("id = ?", id)); err != nil {
+ return nil, err
+ }
+ return status, nil
+}
diff --git a/backend/plugins/teambition/tasks/sprint_collector.go b/backend/plugins/teambition/tasks/sprint_collector.go
new file mode 100644
index 000000000..21ca795df
--- /dev/null
+++ b/backend/plugins/teambition/tasks/sprint_collector.go
@@ -0,0 +1,84 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_SPRINT_TABLE = "teambition_api_sprints"
+
+var _ plugin.SubTaskEntryPoint = CollectSprints
+
+var CollectSprintsMeta = plugin.SubTaskMeta{
+ Name: "collectSprints",
+ EntryPoint: CollectSprints,
+ EnabledByDefault: true,
+ Description: "collect teambition sprints",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectSprints(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_SPRINT_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect sprints")
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ UrlTemplate: "/v3/project/{{ .Params.ProjectId }}/sprint/search",
+ PageSize: int(data.Options.PageSize),
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ data := TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect sprints error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/sprint_convertor.go b/backend/plugins/teambition/tasks/sprint_convertor.go
new file mode 100644
index 000000000..f5d4d41d9
--- /dev/null
+++ b/backend/plugins/teambition/tasks/sprint_convertor.go
@@ -0,0 +1,87 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertSprintsMeta = plugin.SubTaskMeta{
+ Name: "convertSprints",
+ EntryPoint: ConvertSprints,
+ EnabledByDefault: true,
+ Description: "convert teambition projects",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertSprints(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_SPRINT_TABLE)
+ db := taskCtx.GetDal()
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionSprint{}),
+ dal.Where("connection_id = ? AND project_id = ?", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionSprint{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionSprint)
+ sprint := &ticket.Sprint{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getSprintIdGen().Generate(data.Options.ConnectionId, userTool.Id),
+ },
+ Name: userTool.Name,
+ Url: fmt.Sprintf("https://www.teambition.com/project/%s/sprint/section/%s", userTool.ProjectId, userTool.Id),
+ Status: userTool.Status,
+ StartedDate: userTool.StartDate.ToNullableTime(),
+ EndedDate: userTool.DueDate.ToNullableTime(),
+ CompletedDate: userTool.Accomplished.ToNullableTime(),
+ OriginalBoardID: getProjectIdGen().Generate(data.Options.ConnectionId, userTool.ProjectId),
+ }
+
+ return []interface{}{
+ sprint,
+ &ticket.BoardSprint{
+ BoardId: getProjectIdGen().Generate(data.Options.ConnectionId, userTool.ProjectId),
+ SprintId: getSprintIdGen().Generate(data.Options.ConnectionId, userTool.Id),
+ },
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/sprint_extractor.go b/backend/plugins/teambition/tasks/sprint_extractor.go
new file mode 100644
index 000000000..397fe890b
--- /dev/null
+++ b/backend/plugins/teambition/tasks/sprint_extractor.go
@@ -0,0 +1,60 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractSprints
+
+var ExtractSprintsMeta = plugin.SubTaskMeta{
+ Name: "extractSprints",
+ EntryPoint: ExtractSprints,
+ EnabledByDefault: true,
+ Description: "Extract raw data into tool layer table _tool_teambition_sprints",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractSprints(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_SPRINT_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionSprint{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ toolL := userRes
+ toolL.ConnectionId = data.Options.ConnectionId
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_activity_collector.go b/backend/plugins/teambition/tasks/task_activity_collector.go
new file mode 100644
index 000000000..a6034fd9b
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_activity_collector.go
@@ -0,0 +1,104 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/dal"
+ "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/teambition/models"
+ "net/http"
+ "net/url"
+ "reflect"
+)
+
+const RAW_TASK_ACTIVITY_TABLE = "teambition_api_task_activities"
+
+var _ plugin.SubTaskEntryPoint = CollectTaskActivities
+
+var CollectTaskActivitiesMeta = plugin.SubTaskMeta{
+ Name: "collectTaskActivities",
+ EntryPoint: CollectTaskActivities,
+ EnabledByDefault: true,
+ Description: "collect teambition task activities",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectTaskActivities(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_ACTIVITY_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect task activities")
+
+ clauses := []dal.Clause{
+ dal.Select("id as task_id, updated"),
+ dal.From(&models.TeambitionTask{}),
+ dal.Where("_tool_teambition_tasks.connection_id = ? and _tool_teambition_tasks.project_id = ? ", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ db := taskCtx.GetDal()
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ iterator, err := api.NewDalCursorIterator(db, cursor, reflect.TypeOf(models.Input{}))
+ if err != nil {
+ return err
+ }
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ Input: iterator,
+ PageSize: int(data.Options.PageSize),
+ UrlTemplate: "/v3/task/{{ .Input.TaskId }}/activity/list",
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ var data = TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect task activities error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_activity_extractor.go b/backend/plugins/teambition/tasks/task_activity_extractor.go
new file mode 100644
index 000000000..aa1c90a19
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_activity_extractor.go
@@ -0,0 +1,67 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskActivities
+
+var ExtractTaskActivitiesMeta = plugin.SubTaskMeta{
+ Name: "extractTaskActivities",
+ EntryPoint: ExtractTaskActivities,
+ EnabledByDefault: true,
+ Description: "Extract raw company data into tool layer table _tool_teambition_task_activities",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskActivities(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_ACTIVITY_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionTaskActivity{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ input := models.Input{}
+ err = errors.Convert(json.Unmarshal(row.Input, &input))
+ if err != nil {
+ return nil, err
+ }
+ toolL := userRes
+ toolL.ConnectionId = data.Options.ConnectionId
+ toolL.TaskId = input.TaskId
+ toolL.ProjectId = data.Options.ProjectId
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_changelog_converter.go b/backend/plugins/teambition/tasks/task_changelog_converter.go
new file mode 100644
index 000000000..f01076ece
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_changelog_converter.go
@@ -0,0 +1,81 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertTaskChangelogMeta = plugin.SubTaskMeta{
+ Name: "convertTaskChangelog",
+ EntryPoint: ConvertTaskChangelog,
+ EnabledByDefault: true,
+ Description: "convert teambition task changelogs",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertTaskChangelog(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_ACTIVITY_TABLE)
+ db := taskCtx.GetDal()
+ logger := taskCtx.GetLogger()
+ logger.Info("convert project:%v task changelogs", data.Options.ProjectId)
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionTaskActivity{}),
+ dal.Where("connection_id = ? AND project_id = ?", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionTaskActivity{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionTaskActivity)
+ issueComment := &ticket.IssueChangelogs{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getTaskActivityIdGen().Generate(data.Options.ConnectionId, userTool.Id),
+ },
+ IssueId: getTaskIdGen().Generate(userTool.ConnectionId, userTool.TaskId),
+ AuthorId: getAccountIdGen().Generate(userTool.ConnectionId, userTool.CreatorId),
+ CreatedDate: userTool.CreateTime.ToTime(),
+ FieldId: userTool.Action,
+ OriginalToValue: userTool.Content,
+ }
+ return []interface{}{
+ issueComment,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_collector.go b/backend/plugins/teambition/tasks/task_collector.go
new file mode 100644
index 000000000..14ab16557
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_collector.go
@@ -0,0 +1,84 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_TASK_TABLE = "teambition_api_tasks"
+
+var _ plugin.SubTaskEntryPoint = CollectTasks
+
+var CollectTasksMeta = plugin.SubTaskMeta{
+ Name: "collectTasks",
+ EntryPoint: CollectTasks,
+ EnabledByDefault: true,
+ Description: "collect teambition accounts",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectTasks(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect tasks")
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: int(data.Options.PageSize),
+ UrlTemplate: "/v3/project/{{ .Params.ProjectId }}/task/query",
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ var data = TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect task error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_comment_converter.go b/backend/plugins/teambition/tasks/task_comment_converter.go
new file mode 100644
index 000000000..e1bd59d04
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_comment_converter.go
@@ -0,0 +1,86 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertTaskCommentsMeta = plugin.SubTaskMeta{
+ Name: "convertTaskComments",
+ EntryPoint: ConvertTaskComments,
+ EnabledByDefault: true,
+ Description: "convert teambition task comments",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertTaskComments(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_ACTIVITY_TABLE)
+ db := taskCtx.GetDal()
+ logger := taskCtx.GetLogger()
+ logger.Info("convert project:%v task comments", data.Options.ProjectId)
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionTaskActivity{}),
+ dal.Where("connection_id = ? AND project_id = ? AND action = ?", data.Options.ConnectionId, data.Options.ProjectId, "comment"),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionTaskActivity{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionTaskActivity)
+ issueComment := &ticket.IssueComment{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getTaskActivityIdGen().Generate(data.Options.ConnectionId, userTool.Id),
+ },
+ IssueId: getTaskIdGen().Generate(userTool.ConnectionId, userTool.TaskId),
+ AccountId: getAccountIdGen().Generate(userTool.ConnectionId, userTool.CreatorId),
+ CreatedDate: userTool.CreateTime.ToTime(),
+ }
+ comment := &models.TeambitionTaskComment{}
+ err := json.Unmarshal([]byte(userTool.Content), comment)
+ if err != nil {
+ return nil, errors.Convert(err)
+ }
+ issueComment.Body = comment.Comment
+ return []interface{}{
+ issueComment,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_converter.go b/backend/plugins/teambition/tasks/task_converter.go
new file mode 100644
index 000000000..25d6f547e
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_converter.go
@@ -0,0 +1,181 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+ "strconv"
+ "time"
+)
+
+var ConvertTasksMeta = plugin.SubTaskMeta{
+ Name: "convertTasks",
+ EntryPoint: ConvertTasks,
+ EnabledByDefault: true,
+ Description: "convert teambition account",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertTasks(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_TABLE)
+ db := taskCtx.GetDal()
+ logger := taskCtx.GetLogger()
+ logger.Info("convert project:%d", data.Options.ProjectId)
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionTask{}),
+ dal.Where("connection_id = ? AND project_id = ?", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionTask{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionTask)
+ originalEstimateMinutes, timeSpentMinutes, timeRemainingMinutes := calcEstimateTimeMinutes(userTool)
+ issue := &ticket.Issue{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getTaskIdGen().Generate(data.Options.ConnectionId, userTool.Id),
+ },
+ IssueKey: userTool.Id,
+ Title: userTool.Content,
+ Description: userTool.Note,
+ Priority: strconv.Itoa(userTool.Priority),
+ ParentIssueId: userTool.ParentTaskId,
+ CreatorId: userTool.CreatorId,
+ OriginalProject: getProjectIdGen().Generate(data.Options.ConnectionId, data.Options.ProjectId),
+ AssigneeId: userTool.ExecutorId,
+ Url: fmt.Sprintf("https://www.teambition.com/task/%s", userTool.Id),
+ LeadTimeMinutes: calcLeadTimeMinutes(userTool),
+ OriginalEstimateMinutes: originalEstimateMinutes,
+ TimeSpentMinutes: timeSpentMinutes,
+ TimeRemainingMinutes: timeRemainingMinutes,
+ ResolutionDate: userTool.AccomplishTime.ToNullableTime(),
+ CreatedDate: userTool.Created.ToNullableTime(),
+ UpdatedDate: userTool.Updated.ToNullableTime(),
+ }
+ if storyPoint, ok := strconv.ParseFloat(userTool.StoryPoint, 64); ok == nil {
+ issue.StoryPoint = storyPoint
+ }
+ if a, err := FindAccountById(db, userTool.CreatorId); err == nil {
+ issue.CreatorName = a.Name
+ }
+ if a, err := FindAccountById(db, userTool.ExecutorId); err == nil {
+ issue.AssigneeName = a.Name
+ }
+ if p, err := FindProjectById(db, userTool.ProjectId); err == nil {
+ issue.OriginalProject = p.Name
+ }
+
+ stdStatusMappings := getStatusMapping(data)
+ if taskflowstatus, err := FindTaskFlowStatusById(db, userTool.TfsId); err == nil {
+ issue.OriginalStatus = taskflowstatus.Name
+ if v, ok := stdStatusMappings[taskflowstatus.Name]; ok {
+ issue.Status = v
+ } else {
+ switch taskflowstatus.Kind {
+ case "start":
+ issue.Status = ticket.TODO
+ case "unset":
+ issue.Status = ticket.IN_PROGRESS
+ case "end":
+ issue.Status = ticket.DONE
+ }
+ }
+ }
+ stdTypeMappings := getStdTypeMappings(data)
+ if scenario, err := FindTaskScenarioById(db, userTool.SfcId); err == nil {
+ issue.OriginalType = scenario.Name
+ if v, ok := stdTypeMappings[scenario.Name]; ok {
+ issue.Type = v
+ } else {
+ switch scenario.Source {
+ case "application.bug":
+ issue.Type = ticket.BUG
+ case "application.story":
+ issue.Type = ticket.REQUIREMENT
+ case "application.risk":
+ issue.Type = ticket.INCIDENT
+ }
+ }
+ }
+
+ result := make([]interface{}, 0, 3)
+ result = append(result, issue)
+ boardIssue := &ticket.BoardIssue{
+ BoardId: getProjectIdGen().Generate(data.Options.ConnectionId, userTool.ProjectId),
+ IssueId: issue.Id,
+ }
+ result = append(result, boardIssue)
+ if userTool.SprintId != "" {
+ result = append(result, &ticket.SprintIssue{
+ SprintId: getSprintIdGen().Generate(data.Options.ConnectionId, userTool.SprintId),
+ IssueId: issue.Id,
+ })
+ }
+
+ return result, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
+
+func calcLeadTimeMinutes(task *models.TeambitionTask) int64 {
+ if !task.IsDone || task.StartDate == nil || task.AccomplishTime == nil {
+ return 0
+ }
+ startTime := task.StartDate.ToTime()
+ endTime := task.AccomplishTime.ToTime()
+
+ return int64(endTime.Sub(startTime).Minutes())
+}
+
+func calcEstimateTimeMinutes(task *models.TeambitionTask) (
+ originalEstimateMinutes, timeSpentMinutes, timeRemainingMinutes int64) {
+ if task.StartDate == nil || task.DueDate == nil {
+ return 0, 0, 0
+ }
+ startTime := task.StartDate.ToTime()
+ dueTime := task.DueDate.ToTime()
+ originalEstimateMinutes = int64(dueTime.Sub(startTime).Minutes())
+ if task.IsDone {
+ timeSpentMinutes = calcLeadTimeMinutes(task)
+ } else {
+ timeSpentMinutes = int64(time.Since(startTime).Minutes())
+ }
+ timeRemainingMinutes = originalEstimateMinutes - timeSpentMinutes
+ return
+}
diff --git a/backend/plugins/teambition/tasks/task_data.go b/backend/plugins/teambition/tasks/task_data.go
new file mode 100644
index 000000000..6f4f30747
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_data.go
@@ -0,0 +1,66 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/core/errors"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "time"
+)
+
+type TeambitionOptions struct {
+ ConnectionId uint64 `json:"connectionId"`
+ ProjectId string `json:"projectId"`
+ PageSize uint64 `mapstruct:"pageSize"`
+ TimeAfter string `json:"timeAfter" mapstructure:"timeAfter,omitempty"`
+ CstZone *time.Location
+ TransformationRules TransformationRules `json:"transformationRules"`
+}
+
+type TeambitionTaskData struct {
+ Options *TeambitionOptions
+ ApiClient *helper.ApiAsyncClient
+ TimeAfter *time.Time
+ TenantId string
+}
+
+func DecodeAndValidateTaskOptions(options map[string]interface{}) (*TeambitionOptions, errors.Error) {
+ var op TeambitionOptions
+ if err := helper.Decode(options, &op, nil); err != nil {
+ return nil, err
+ }
+ if op.ConnectionId == 0 {
+ return nil, errors.Default.New("connectionId is invalid")
+ }
+ return &op, nil
+}
+
+type TypeMapping struct {
+ StandardType string `json:"standardType"`
+}
+
+type OriginalStatus []string
+
+type StatusMappings map[string]OriginalStatus
+
+type TypeMappings map[string]TypeMapping
+
+type TransformationRules struct {
+ TypeMappings TypeMappings `json:"typeMappings"`
+ StatusMappings StatusMappings `json:"statusMappings"`
+}
diff --git a/backend/plugins/teambition/tasks/task_extractor.go b/backend/plugins/teambition/tasks/task_extractor.go
new file mode 100644
index 000000000..a2c5a721b
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_extractor.go
@@ -0,0 +1,69 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTasks
+
+var ExtractTasksMeta = plugin.SubTaskMeta{
+ Name: "extractTasks",
+ EntryPoint: ExtractTasks,
+ EnabledByDefault: true,
+ Description: "Extract raw data into tool layer table _tool_teambition_tasks",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTasks(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionTask{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ results := make([]interface{}, 0)
+ toolL := userRes
+ toolL.ConnectionId = data.Options.ConnectionId
+ results = append(results, &toolL)
+ for _, tagId := range userRes.TagIds {
+ taskTag := &models.TeambitionTaskTagTask{
+ ConnectionId: data.Options.ConnectionId,
+ TaskId: userRes.Id,
+ TaskTagId: tagId,
+ ProjectId: userRes.ProjectId,
+ }
+ results = append(results, taskTag)
+ }
+ return results, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_flow_status_collector.go b/backend/plugins/teambition/tasks/task_flow_status_collector.go
new file mode 100644
index 000000000..42e31b360
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_flow_status_collector.go
@@ -0,0 +1,84 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_TASK_FLOW_STATUS_TABLE = "teambition_api_task_flow_status"
+
+var _ plugin.SubTaskEntryPoint = CollectTaskFlowStatus
+
+var CollectTaskFlowStatusMeta = plugin.SubTaskMeta{
+ Name: "collect task flow status",
+ EntryPoint: CollectTaskFlowStatus,
+ EnabledByDefault: true,
+ Description: "collect teambition task flow status",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectTaskFlowStatus(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_FLOW_STATUS_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect projects")
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ UrlTemplate: "/v3/project/{{ .Params.ProjectId }}/taskflowstatus/search",
+ PageSize: int(data.Options.PageSize),
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ data := TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect task work flow status error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_flow_status_extractor.go b/backend/plugins/teambition/tasks/task_flow_status_extractor.go
new file mode 100644
index 000000000..2aef6acc4
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_flow_status_extractor.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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskFlowStatus
+
+var ExtractTaskFlowStatusMeta = plugin.SubTaskMeta{
+ Name: "extractTaskWorkFlowStatus",
+ EntryPoint: ExtractTaskFlowStatus,
+ EnabledByDefault: true,
+ Description: "Extract raw data into tool layer table _tool_teambition_task_work_flow_status",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskFlowStatus(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_FLOW_STATUS_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionTaskFlowStatus{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ toolL := userRes
+ toolL.ProjectId = data.Options.ProjectId
+ toolL.ConnectionId = data.Options.ConnectionId
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_scenario_collector.go b/backend/plugins/teambition/tasks/task_scenario_collector.go
new file mode 100644
index 000000000..5f89b1343
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_scenario_collector.go
@@ -0,0 +1,84 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_TASK_SCENARIOS_TABLE = "teambition_api_task_scenarios"
+
+var _ plugin.SubTaskEntryPoint = CollectTaskScenarios
+
+var CollectTaskScenariosMeta = plugin.SubTaskMeta{
+ Name: "collect task flow status",
+ EntryPoint: CollectTaskScenarios,
+ EnabledByDefault: true,
+ Description: "collect teambition task flow scenarios",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectTaskScenarios(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_SCENARIOS_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect projects")
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ UrlTemplate: "/v3/project/{{ .Params.ProjectId }}/scenariofieldconfig/search",
+ PageSize: int(data.Options.PageSize),
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ data := TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect task scenarios error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_scenario_extractor.go b/backend/plugins/teambition/tasks/task_scenario_extractor.go
new file mode 100644
index 000000000..10214fa1f
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_scenario_extractor.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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskScenarios
+
+var ExtractTaskScenariosMeta = plugin.SubTaskMeta{
+ Name: "extractTaskScenarios",
+ EntryPoint: ExtractTaskScenarios,
+ EnabledByDefault: true,
+ Description: "Extract raw data into tool layer table _tool_teambition_task_scenarios",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskScenarios(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_SCENARIOS_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionTaskScenario{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ toolL := userRes
+ toolL.ConnectionId = data.Options.ConnectionId
+ toolL.ProjectId = data.Options.ProjectId
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_tag_collector.go b/backend/plugins/teambition/tasks/task_tag_collector.go
new file mode 100644
index 000000000..f3eb3897a
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_tag_collector.go
@@ -0,0 +1,84 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "net/http"
+ "net/url"
+)
+
+const RAW_TASK_TAG_TABLE = "teambition_api_task_tags"
+
+var _ plugin.SubTaskEntryPoint = CollectTasks
+
+var CollectTaskTagsMeta = plugin.SubTaskMeta{
+ Name: "collectTaskTags",
+ EntryPoint: CollectTaskTags,
+ EnabledByDefault: true,
+ Description: "collect teambition task tags",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectTaskTags(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_TAG_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect task tags")
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: int(data.Options.PageSize),
+ UrlTemplate: "/v3/project/{{ .Params.ProjectId }}/tag/search",
+ GetNextPageCustomData: func(prevReqData *api.RequestData, prevPageResponse *http.Response) (interface{}, errors.Error) {
+ res := TeambitionComRes[any]{}
+ err := api.UnmarshalResponse(prevPageResponse, &res)
+ if err != nil {
+ return nil, err
+ }
+ if res.NextPageToken == "" {
+ return nil, api.ErrFinishCollect
+ }
+ return res.NextPageToken, nil
+ },
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ if data.Options.PageSize > 0 {
+ query.Set("pageSize", fmt.Sprintf("%v", data.Options.PageSize))
+ }
+ if pageToken, ok := reqData.CustomData.(string); ok && pageToken != "" {
+ query.Set("pageToken", reqData.CustomData.(string))
+ }
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ var data = TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect task error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_tag_extractor.go b/backend/plugins/teambition/tasks/task_tag_extractor.go
new file mode 100644
index 000000000..423d7432a
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_tag_extractor.go
@@ -0,0 +1,67 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskTags
+
+var ExtractTaskTagsMeta = plugin.SubTaskMeta{
+ Name: "extractTaskTags",
+ EntryPoint: ExtractTaskTags,
+ EnabledByDefault: true,
+ Description: "Extract raw data into tool layer table _tool_teambition_task_tags",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskTags(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_TAG_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionTaskTag{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ toolL := models.TeambitionTaskTag{
+ ConnectionId: data.Options.ConnectionId,
+ Id: userRes.Id,
+ Name: userRes.Name,
+ ProjectId: userRes.ProjectId,
+ IsArchived: userRes.IsArchived,
+ Updated: userRes.Updated,
+ Created: userRes.Created,
+ }
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_tag_task_converter.go b/backend/plugins/teambition/tasks/task_tag_task_converter.go
new file mode 100644
index 000000000..d8710b302
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_tag_task_converter.go
@@ -0,0 +1,80 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertTaskTagTasksMeta = plugin.SubTaskMeta{
+ Name: "convertTaskTags",
+ EntryPoint: ConvertTaskTagTasks,
+ EnabledByDefault: true,
+ Description: "convert teambition task tags",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertTaskTagTasks(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_TAG_TABLE)
+ db := taskCtx.GetDal()
+ logger := taskCtx.GetLogger()
+ logger.Info("convert project:%v task tag tasks", data.Options.ProjectId)
+ clauses := []dal.Clause{
+ dal.Select("b.name as name, a.task_id as task_id, a.connection_id as connection_id, a.project_id as project_id"),
+ dal.From("_tool_teambition_task_tag_tasks a"),
+ dal.Join(`left join _tool_teambition_task_tags b on (
+ a.connection_id = b.connection_id
+ AND a.project_id = b.project_id
+ AND a.task_tag_id = b.id
+ )`),
+ dal.Where("a.connection_id = ? AND a.project_id = ?", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionTaskTagTask{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionTaskTagTask)
+ issue := &ticket.IssueLabel{
+ IssueId: getTaskIdGen().Generate(userTool.ConnectionId, userTool.TaskId),
+ LabelName: userTool.Name,
+ }
+ return []interface{}{
+ issue,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_worktime_collector.go b/backend/plugins/teambition/tasks/task_worktime_collector.go
new file mode 100644
index 000000000..7d19096e5
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_worktime_collector.go
@@ -0,0 +1,85 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/core/dal"
+ "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/teambition/models"
+ "net/http"
+ "net/url"
+ "reflect"
+)
+
+const RAW_TASK_WORKTIME_TABLE = "teambition_api_task_worktime"
+
+var _ plugin.SubTaskEntryPoint = CollectTaskWorktime
+
+var CollectTaskWorktimeMeta = plugin.SubTaskMeta{
+ Name: "collectTaskWorktime",
+ EntryPoint: CollectTaskWorktime,
+ EnabledByDefault: true,
+ Description: "collect teambition task worktime",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func CollectTaskWorktime(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_WORKTIME_TABLE)
+ logger := taskCtx.GetLogger()
+ logger.Info("collect task worktime")
+
+ clauses := []dal.Clause{
+ dal.Select("id as task_id, updated"),
+ dal.From(&models.TeambitionTask{}),
+ dal.Where("_tool_teambition_tasks.connection_id = ? and _tool_teambition_tasks.project_id = ? ", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ db := taskCtx.GetDal()
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ iterator, err := api.NewDalCursorIterator(db, cursor, reflect.TypeOf(models.Input{}))
+ if err != nil {
+ return err
+ }
+
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ Input: iterator,
+ UrlTemplate: "/worktime/list/task/{{ .Input.TaskId }}",
+ Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
+ query := url.Values{}
+ return query, nil
+ },
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
+ var data = TeambitionComRes[[]json.RawMessage]{}
+ err := api.UnmarshalResponse(res, &data)
+ return data.Result, err
+ },
+ })
+ if err != nil {
+ logger.Error(err, "collect task worktime error")
+ return err
+ }
+ return collector.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_worktime_converter.go b/backend/plugins/teambition/tasks/task_worktime_converter.go
new file mode 100644
index 000000000..7983934c8
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_worktime_converter.go
@@ -0,0 +1,82 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/teambition/models"
+ "reflect"
+)
+
+var ConvertTaskWorktimeMeta = plugin.SubTaskMeta{
+ Name: "convertTaskWorktime",
+ EntryPoint: ConvertTaskWorktime,
+ EnabledByDefault: true,
+ Description: "convert teambition task worktime",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertTaskWorktime(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_WORKTIME_TABLE)
+ db := taskCtx.GetDal()
+ logger := taskCtx.GetLogger()
+ logger.Info("convert project:%v task worktime", data.Options.ProjectId)
+ clauses := []dal.Clause{
+ dal.From(&models.TeambitionTaskWorktime{}),
+ dal.Where("connection_id = ? AND project_id = ?", data.Options.ConnectionId, data.Options.ProjectId),
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ InputRowType: reflect.TypeOf(models.TeambitionTaskWorktime{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
+ userTool := inputRow.(*models.TeambitionTaskWorktime)
+ issueWorklog := &ticket.IssueWorklog{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: getTaskWorktimeIdGen().Generate(data.Options.ConnectionId, userTool.WorktimeId),
+ },
+ IssueId: getTaskIdGen().Generate(userTool.ConnectionId, userTool.TaskId),
+ AuthorId: getAccountIdGen().Generate(userTool.ConnectionId, userTool.UserId),
+ LoggedDate: userTool.CreatedAt.ToNullableTime(),
+ Comment: userTool.Description,
+ TimeSpentMinutes: int(userTool.Worktime / (60 * 1000)),
+ StartedDate: userTool.Date.ToNullableTime(),
+ }
+ return []interface{}{
+ issueWorklog,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/teambition/tasks/task_worktime_extractor.go b/backend/plugins/teambition/tasks/task_worktime_extractor.go
new file mode 100644
index 000000000..e42b385af
--- /dev/null
+++ b/backend/plugins/teambition/tasks/task_worktime_extractor.go
@@ -0,0 +1,67 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "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/teambition/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskWorktime
+
+var ExtractTaskWorktimeMeta = plugin.SubTaskMeta{
+ Name: "extractTaskWorktime",
+ EntryPoint: ExtractTaskWorktime,
+ EnabledByDefault: true,
+ Description: "Extract raw company data into tool layer table _tool_teambition_task_worktime",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskWorktime(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_TASK_WORKTIME_TABLE)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ userRes := models.TeambitionTaskWorktime{}
+ err := errors.Convert(json.Unmarshal(row.Data, &userRes))
+ if err != nil {
+ return nil, err
+ }
+ input := models.Input{}
+ err = errors.Convert(json.Unmarshal(row.Input, &input))
+ if err != nil {
+ return nil, err
+ }
+ toolL := userRes
+ toolL.ConnectionId = data.Options.ConnectionId
+ toolL.TaskId = input.TaskId
+ toolL.ProjectId = data.Options.ProjectId
+ return []interface{}{
+ &toolL,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/teambition/teambition.go b/backend/plugins/teambition/teambition.go
new file mode 100644
index 000000000..fde97b831
--- /dev/null
+++ b/backend/plugins/teambition/teambition.go
@@ -0,0 +1,43 @@
+/*
+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 main
+
+import (
+ "github.com/apache/incubator-devlake/core/runner"
+ "github.com/apache/incubator-devlake/plugins/teambition/impl"
+ "github.com/spf13/cobra"
+)
+
+// PluginEntry Export a variable named PluginEntry for Framework to search and load
+var PluginEntry impl.Teambition //nolint
+
+// standalone mode for debugging
+func main() {
+ cmd := &cobra.Command{Use: "teambition"}
+ connectionId := cmd.Flags().Uint64P("connection", "c", 0, "teambition connection id")
+ projectId := cmd.Flags().StringP("project", "p", "", "teambition project id")
+
+ cmd.Run = func(cmd *cobra.Command, args []string) {
+ runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
+ "connectionId": *connectionId,
+ "projectId": *projectId,
+ })
+ }
+
+ runner.RunCmd(cmd)
+}
diff --git a/grafana/dashboards/Teambition.json b/grafana/dashboards/Teambition.json
new file mode 100644
index 000000000..44133f9d7
--- /dev/null
+++ b/grafana/dashboards/Teambition.json
@@ -0,0 +1,1031 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 22,
+ "iteration": 1672982963558,
+ "links": [
+ {
+ "asDropdown": false,
+ "icon": "bolt",
+ "includeVars": false,
+ "keepTime": true,
+ "tags": [],
+ "targetBlank": false,
+ "title": "Homepage",
+ "tooltip": "",
+ "type": "link",
+ "url": "/grafana/d/Lv1XbLHnk/data-specific-dashboards-homepage"
+ },
+ {
+ "asDropdown": false,
+ "icon": "external link",
+ "includeVars": false,
+ "keepTime": true,
+ "tags": [
+ "Data Source Specific Dashboard"
+ ],
+ "targetBlank": false,
+ "title": "Metric dashboards",
+ "tooltip": "",
+ "type": "dashboards",
+ "url": ""
+ }
+ ],
+ "panels": [
+ {
+ "datasource": null,
+ "gridPos": {
+ "h": 3,
+ "w": 13,
+ "x": 0,
+ "y": 0
+ },
+ "id": 128,
+ "options": {
+ "content": "- Use Cases: This dashboard shows the basic project management metrics from Teambition.\n- Data Source Required: Teambition",
+ "mode": "markdown"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "queryType": "randomWalk",
+ "refId": "A"
+ }
+ ],
+ "title": "Dashboard Introduction",
+ "type": "text"
+ },
+ {
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 3
+ },
+ "id": 126,
+ "title": "1. Issue Throughput",
+ "type": "row"
+ },
+ {
+ "datasource": "mysql",
+ "description": "1. Total number of issues created.\n2. The requirements being calculated are filtered by \"requirement creation time\" (time filter at the upper-right corner) and \"Jira board\" (\"Choose Board\" filter at the upper-left corner)",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 0,
+ "y": 4
+ },
+ "id": 114,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "select \r\n count(*) as value\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere \r\n i.type in ($type)\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in ($board_id)",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Number of Issues [Issues Created in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 4,
+ "y": 4
+ },
+ "id": 116,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "select \r\n count(*) as value\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere \r\n i.type in ($type)\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in ($board_id)",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Number of Delivered Issue [Issues Created in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 1,
+ "drawStyle": "bars",
+ "fillOpacity": 12,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 4
+ },
+ "id": 120,
+ "interval": null,
+ "options": {
+ "legend": {
+ "calcs": [
+ "sum"
+ ],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "SELECT\r\n DATE_ADD(date(i.created_date), INTERVAL -DAYOFMONTH(date(i.created_date))+1 DAY) as time,\r\n count(distinct case when status != 'DONE' then i.id else null end) as \"Number of Open Issues\",\r\n count(distinct case when status = 'DONE' then i.id else null end) as \"Number of Delivered Issues\"\r\nFROM issues i\r\n\tjoin board_issues bi on i.id = bi.issue_id\r\n\tjoin boards b on bi.board_id = b.id\r\nwhere \r\n i.type in ($type)\r\n and $__timeFilter( [...]
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Issue Status Distribution over Month [Issues Created in Selected Time Range]",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "mysql",
+ "description": "Issue Delivery Rate = count(Delivered Issues)/count(Issues)",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "percentage",
+ "steps": [
+ {
+ "color": "red",
+ "value": null
+ },
+ {
+ "color": "green",
+ "value": 50
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 10
+ },
+ "id": 117,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "with _requirements as(\r\n select\r\n count(distinct i.id) as total_count,\r\n count(distinct case when i.status = 'DONE' then i.id else null end) as delivered_count\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where \r\n i.type in ($type)\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in ($board_id)\r\n)\r\n\r\nselect \r\n now() as time,\r\n 1.0 * delivered_count/total_count as requirement_delivery_rate\r\nfr [...]
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "title": "Issue Delivery Rate [Issues Created in Selected Time Range]",
+ "type": "stat"
+ },
+ {
+ "cacheTimeout": null,
+ "datasource": "mysql",
+ "description": "Issue Delivery Rate = count(Delivered Issues)/count(Issues)",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "Delivery Rate(%)",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 12,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 10
+ },
+ "id": 121,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "single"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "queryType": "randomWalk",
+ "rawQuery": true,
+ "rawSql": "with _requirements as(\r\n select\r\n DATE_ADD(date(i.created_date), INTERVAL -DAYOFMONTH(date(i.created_date))+1 DAY) as time,\r\n 1.0 * count(distinct case when i.status = 'DONE' then i.id else null end)/count(distinct i.id) as delivered_rate\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where \r\n i.type in ($type)\r\n and $__timeFilter(i.created_date)\r\n and bi.board_id in ($board_id)\r\n group by 1\r\n)\r\n\r\nselect [...]
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "timeColumn": "time",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Issue Delivery Rate over Time [Issues Created in Selected Time Range]",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 16
+ },
+ "id": 110,
+ "panels": [],
+ "title": "2. Issue Lead Time",
+ "type": "row"
+ },
+ {
+ "datasource": "mysql",
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 1,
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 14
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 0,
+ "y": 17
+ },
+ "id": 12,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "mean"
+ ],
+ "fields": "/^value$/",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "select \r\n avg(lead_time_minutes/1440) as value\r\nfrom issues i\r\n join board_issues bi on i.id = bi.issue_id\r\nwhere \r\n i.type in ($type)\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in ($board_id)",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "table": "ca_analysis",
+ "timeColumn": "create_time",
+ "timeColumnType": "timestamp",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Mean Issue Lead Time in Days [Issues Resolved in Select Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 21
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 4,
+ "x": 4,
+ "y": 17
+ },
+ "id": 13,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "with _ranks as(\r\n select \r\n i.lead_time_minutes,\r\n percent_rank() over (order by lead_time_minutes asc) as ranks\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where \r\n i.type in ($type)\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in ($board_id)\r\n)\r\n\r\nselect\r\n max(lead_time_minutes/1440) as value\r\nfrom _ranks\r\nwhere \r\n ranks <= 0.8",
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "table": "ca_analysis",
+ "timeColumn": "create_time",
+ "timeColumnType": "timestamp",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "80% Issues' Lead Time are less than # days [Issues Resolved in Select Time Range]",
+ "type": "stat"
+ },
+ {
+ "datasource": "mysql",
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "Lead Time(days)",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 17
+ },
+ "id": 17,
+ "interval": "",
+ "options": {
+ "barWidth": 0.5,
+ "groupWidth": 0.7,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "orientation": "auto",
+ "showValue": "auto",
+ "text": {
+ "valueSize": 12
+ },
+ "tooltip": {
+ "mode": "single"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "format": "table",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "with _requirements as(\r\n select \r\n DATE_ADD(date(i.resolution_date), INTERVAL -DAYOFMONTH(date(i.resolution_date))+1 DAY) as time,\r\n avg(lead_time_minutes/1440) as mean_lead_time\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where \r\n i.type in ($type)\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in ($board_id)\r\n group by 1\r\n)\r\n\r\nselect \r\n date_format(time,'%M [...]
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "table": "ca_analysis",
+ "timeColumn": "create_time",
+ "timeColumnType": "timestamp",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Mean Issue Lead Time [Issues Resolved in Select Time Range]",
+ "type": "barchart"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "mysql",
+ "description": "1. The cumulative distribution of requirement lead time. \n2. Each point refers to the percent rank of a lead time.",
+ "fill": 0,
+ "fillGradient": 4,
+ "gridPos": {
+ "h": 6,
+ "w": 24,
+ "x": 0,
+ "y": 23
+ },
+ "hiddenSeries": false,
+ "id": 15,
+ "legend": {
+ "alignAsTable": false,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 8,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": false
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.6",
+ "pointradius": 0.5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "format": "time_series",
+ "group": [],
+ "metricColumn": "none",
+ "rawQuery": true,
+ "rawSql": "with _ranks as(\r\n select \r\n round(i.lead_time_minutes/1440) as lead_time_day\r\n from issues i\r\n join board_issues bi on i.id = bi.issue_id\r\n where \r\n i.type in ($type)\r\n and i.status = 'DONE'\r\n and $__timeFilter(i.resolution_date)\r\n and bi.board_id in ($board_id)\r\n order by lead_time_day asc\r\n)\r\n\r\nselect \r\n now() as time,\r\n lpad(concat(lead_time_day,'d'), 4, ' ') as metric,\r\n percent_rank() over (order by lead_t [...]
+ "refId": "A",
+ "select": [
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "column"
+ }
+ ]
+ ],
+ "table": "ca_analysis",
+ "timeColumn": "create_time",
+ "timeColumnType": "timestamp",
+ "where": [
+ {
+ "name": "$__timeFilter",
+ "params": [],
+ "type": "macro"
+ }
+ ]
+ }
+ ],
+ "thresholds": [
+ {
+ "$$hashKey": "object:469",
+ "colorMode": "ok",
+ "fill": true,
+ "line": true,
+ "op": "lt",
+ "value": 0.8,
+ "yaxis": "right"
+ }
+ ],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Cumulative Distribution of Requirement Lead Time [Issues Resolved in Select Time Range]",
+ "tooltip": {
+ "shared": false,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "transformations": [],
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "series",
+ "name": null,
+ "show": true,
+ "values": [
+ "current"
+ ]
+ },
+ "yaxes": [
+ {
+ "$$hashKey": "object:76",
+ "format": "percentunit",
+ "label": "Percent Rank (%)",
+ "logBase": 1,
+ "max": "1.2",
+ "min": null,
+ "show": true
+ },
+ {
+ "$$hashKey": "object:77",
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": false
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "datasource": null,
+ "gridPos": {
+ "h": 2,
+ "w": 24,
+ "x": 0,
+ "y": 29
+ },
+ "id": 130,
+ "options": {
+ "content": "<br/>\n\nThis dashboard is created based on this [data schema](https://devlake.apache.org/docs/DataModels/DevLakeDomainLayerSchema). Want to add more metrics? Please follow the [guide](https://devlake.apache.org/docs/Configuration/Dashboards/GrafanaUserGuide).",
+ "mode": "markdown"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "queryType": "randomWalk",
+ "refId": "A"
+ }
+ ],
+ "type": "text"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 30,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {
+ "selected": true,
+ "text": [
+ "All"
+ ],
+ "value": [
+ "$__all"
+ ]
+ },
+ "datasource": "mysql",
+ "definition": "select concat(name, '--', id) from boards where id like 'teambition%'",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Choose Board",
+ "multi": true,
+ "name": "board_id",
+ "options": [],
+ "query": "select concat(name, '--', id) from boards where id like 'teambition%'",
+ "refresh": 1,
+ "regex": "/^(?<text>.*)--(?<value>.*)$/",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ },
+ {
+ "allValue": null,
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": "mysql",
+ "definition": "select distinct type from issues",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Issue Type",
+ "multi": false,
+ "name": "type",
+ "options": [],
+ "query": "select distinct type from issues",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-6M",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Teambition",
+ "uid": "hi-908hVk",
+ "version": 3
+}
\ No newline at end of file