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

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

commit 43707f8850ce36233ecfbd5e03eca8a6c4e6c87b
Author: zhangliang <[email protected]>
AuthorDate: Thu Jun 23 21:00:41 2022 +0800

    feat: project,board,issue
---
 plugins/jira/e2e/board_test.go                     |  80 +++++++++
 plugins/jira/e2e/issue_test.go                     | 151 +++++++++++++++++
 plugins/jira/e2e/project_test.go                   |  61 +++++++
 .../jira/e2e/raw_tables/_raw_jira_api_boards.csv   |   2 +
 .../jira/e2e/raw_tables/_raw_jira_api_issues.csv   |  31 ++++
 .../jira/e2e/raw_tables/_raw_jira_api_projects.csv |  13 ++
 .../snapshot_tables/_tool_jira_board_issues.csv    |  31 ++++
 .../jira/e2e/snapshot_tables/_tool_jira_boards.csv |   2 +
 .../jira/e2e/snapshot_tables/_tool_jira_issues.csv |  31 ++++
 .../e2e/snapshot_tables/_tool_jira_projects.csv    |  13 ++
 plugins/jira/e2e/snapshot_tables/board_issues.csv  |  31 ++++
 plugins/jira/e2e/snapshot_tables/boards.csv        |   2 +
 plugins/jira/e2e/snapshot_tables/issues.csv        |  31 ++++
 plugins/jira/{jira.go => impl/impl.go}             |  79 ++++-----
 plugins/jira/jira.go                               | 181 +--------------------
 plugins/jira/tasks/board_collector.go              |   7 +
 plugins/jira/tasks/board_convertor.go              |   7 +
 plugins/jira/tasks/board_extractor.go              |   6 +
 plugins/jira/tasks/changelog_collector.go          |   7 +
 plugins/jira/tasks/changelog_convertor.go          |   7 +
 plugins/jira/tasks/changelog_extractor.go          |   7 +
 plugins/jira/tasks/issue_collector.go              |   7 +
 plugins/jira/tasks/issue_commit_convertor.go       |   7 +
 plugins/jira/tasks/issue_convertor.go              |   7 +
 plugins/jira/tasks/issue_extractor.go              |   7 +
 plugins/jira/tasks/issue_repo_commit_convertor.go  |   7 +
 plugins/jira/tasks/project_collector.go            |   7 +
 plugins/jira/tasks/project_extractor.go            |   7 +
 plugins/jira/tasks/remotelink_collector.go         |   7 +
 plugins/jira/tasks/remotelink_extractor.go         |   7 +
 plugins/jira/tasks/sprint_collector.go             |   7 +
 plugins/jira/tasks/sprint_convertor.go             |   7 +
 plugins/jira/tasks/sprint_extractor.go             |   7 +
 plugins/jira/tasks/sprint_issues_convertor.go      |   7 +
 plugins/jira/tasks/status_collector.go             |   7 +
 plugins/jira/tasks/status_extractor.go             |   7 +
 plugins/jira/tasks/user_collector.go               |   7 +
 plugins/jira/tasks/user_convertor.go               |   7 +
 plugins/jira/tasks/user_extractor.go               |   7 +
 plugins/jira/tasks/worklog_collector.go            |   7 +
 plugins/jira/tasks/worklog_convertor.go            |   7 +
 plugins/jira/tasks/worklog_extractor.go            |   7 +
 42 files changed, 697 insertions(+), 230 deletions(-)

diff --git a/plugins/jira/e2e/board_test.go b/plugins/jira/e2e/board_test.go
new file mode 100644
index 00000000..ef0af874
--- /dev/null
+++ b/plugins/jira/e2e/board_test.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 e2e
+
+import (
+       "testing"
+
+       "github.com/apache/incubator-devlake/helpers/e2ehelper"
+       "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+       "github.com/apache/incubator-devlake/plugins/jira/impl"
+       "github.com/apache/incubator-devlake/plugins/jira/models"
+       "github.com/apache/incubator-devlake/plugins/jira/tasks"
+)
+
+func TestBoardDataFlow(t *testing.T) {
+       var plugin impl.Jira
+       dataflowTester := e2ehelper.NewDataFlowTester(t, "jira", plugin)
+
+       taskData := &tasks.JiraTaskData{
+               Options: &tasks.JiraOptions{
+                       ConnectionId: 2,
+                       BoardId:      8,
+               },
+       }
+
+       // import raw data table
+       
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jira_api_boards.csv", 
"_raw_jira_api_boards")
+
+       // verify board extraction
+       dataflowTester.FlushTabler(&models.JiraBoard{})
+       dataflowTester.Subtask(tasks.ExtractBoardMeta, taskData)
+       dataflowTester.VerifyTable(
+               models.JiraBoard{},
+               "./snapshot_tables/_tool_jira_boards.csv",
+               []string{"connection_id", "board_id"},
+               []string{
+                       "connection_id",
+                       "board_id",
+                       "project_id",
+                       "name",
+                       "self",
+                       "type",
+                       "_raw_data_params",
+                       "_raw_data_table",
+                       "_raw_data_id",
+                       "_raw_data_remark",
+               },
+       )
+
+       // verify board conversion
+       dataflowTester.FlushTabler(&ticket.Board{})
+       dataflowTester.Subtask(tasks.ConvertBoardMeta, taskData)
+       dataflowTester.VerifyTable(
+               ticket.Board{},
+               "./snapshot_tables/boards.csv",
+               []string{"id"},
+               []string{
+                       "id",
+                       "name",
+                       "description",
+                       "url",
+                       "created_date",
+               },
+       )
+}
diff --git a/plugins/jira/e2e/issue_test.go b/plugins/jira/e2e/issue_test.go
new file mode 100644
index 00000000..767afada
--- /dev/null
+++ b/plugins/jira/e2e/issue_test.go
@@ -0,0 +1,151 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+       "testing"
+
+       "github.com/apache/incubator-devlake/helpers/e2ehelper"
+       "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+       "github.com/apache/incubator-devlake/plugins/jira/impl"
+       "github.com/apache/incubator-devlake/plugins/jira/models"
+       "github.com/apache/incubator-devlake/plugins/jira/tasks"
+)
+
+func TestIssueDataFlow(t *testing.T) {
+       var plugin impl.Jira
+       dataflowTester := e2ehelper.NewDataFlowTester(t, "jira", plugin)
+
+       taskData := &tasks.JiraTaskData{
+               Options: &tasks.JiraOptions{
+                       ConnectionId: 2,
+                       BoardId:      8,
+               },
+       }
+
+       // import raw data table
+       
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jira_api_issues.csv", 
"_raw_jira_api_issues")
+
+       // verify issue extraction
+       dataflowTester.FlushTabler(&models.JiraIssue{})
+       dataflowTester.FlushTabler(&models.JiraBoardIssue{})
+       dataflowTester.FlushTabler(&models.JiraSprintIssue{})
+       dataflowTester.FlushTabler(&models.JiraChangelog{})
+       dataflowTester.FlushTabler(&models.JiraChangelogItem{})
+       dataflowTester.FlushTabler(&models.JiraWorklog{})
+       dataflowTester.FlushTabler(&models.JiraUser{})
+       dataflowTester.Subtask(tasks.ExtractIssuesMeta, taskData)
+       dataflowTester.VerifyTable(
+               models.JiraIssue{},
+               "./snapshot_tables/_tool_jira_issues.csv",
+               []string{"connection_id", "issue_id"},
+               []string{
+                       "connection_id",
+                       "issue_id",
+                       "project_id",
+                       "self",
+                       "issue_key",
+                       "summary",
+                       "type",
+                       "epic_key",
+                       "status_name",
+                       "status_key",
+                       "story_point",
+                       "original_estimate_minutes",
+                       "aggregate_estimate_minutes",
+                       "remaining_estimate_minutes",
+                       "creator_account_id",
+                       "creator_account_type",
+                       "creator_display_name",
+                       "assignee_account_id",
+                       "assignee_account_type",
+                       "assignee_display_name",
+                       "priority_id",
+                       "priority_name",
+                       "parent_id",
+                       "parent_key",
+                       "sprint_id",
+                       "sprint_name",
+                       "resolution_date",
+                       "created",
+                       "updated",
+                       "spent_minutes",
+                       "lead_time_minutes",
+                       "std_story_point",
+                       "std_type",
+                       "std_status",
+                       "created_at",
+                       "updated_at",
+                       "_raw_data_params",
+                       "_raw_data_table",
+                       "_raw_data_id",
+                       "_raw_data_remark",
+                       "icon_url",
+               },
+       )
+
+       dataflowTester.VerifyTable(
+               models.JiraBoardIssue{},
+               "./snapshot_tables/_tool_jira_board_issues.csv",
+               []string{"connection_id", "board_id", "issue_id"},
+               []string{},
+       )
+
+       // verify issue conversion
+       dataflowTester.FlushTabler(&ticket.Issue{})
+       dataflowTester.FlushTabler(&ticket.BoardIssue{})
+       dataflowTester.Subtask(tasks.ConvertIssuesMeta, taskData)
+       dataflowTester.VerifyTable(
+               ticket.Issue{},
+               "./snapshot_tables/issues.csv",
+               []string{"id"},
+               []string{
+                       "url",
+                       "icon_url",
+                       "issue_key",
+                       "title",
+                       "description",
+                       "epic_key",
+                       "type",
+                       "status",
+                       "original_status",
+                       "story_point",
+                       "resolution_date",
+                       "created_date",
+                       "updated_date",
+                       "lead_time_minutes",
+                       "parent_issue_id",
+                       "priority",
+                       "original_estimate_minutes",
+                       "time_spent_minutes",
+                       "time_remaining_minutes",
+                       "creator_id",
+                       "creator_name",
+                       "assignee_id",
+                       "assignee_name",
+                       "severity",
+                       "component",
+               },
+       )
+       dataflowTester.VerifyTable(
+               ticket.BoardIssue{},
+               "./snapshot_tables/board_issues.csv",
+               []string{"board_id", "issue_id"},
+               []string{},
+       )
+}
diff --git a/plugins/jira/e2e/project_test.go b/plugins/jira/e2e/project_test.go
new file mode 100644
index 00000000..3669c0d7
--- /dev/null
+++ b/plugins/jira/e2e/project_test.go
@@ -0,0 +1,61 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+       "testing"
+
+       "github.com/apache/incubator-devlake/helpers/e2ehelper"
+       "github.com/apache/incubator-devlake/plugins/jira/impl"
+       "github.com/apache/incubator-devlake/plugins/jira/models"
+       "github.com/apache/incubator-devlake/plugins/jira/tasks"
+)
+
+func TestProjectDataFlow(t *testing.T) {
+       var plugin impl.Jira
+       dataflowTester := e2ehelper.NewDataFlowTester(t, "jira", plugin)
+
+       taskData := &tasks.JiraTaskData{
+               Options: &tasks.JiraOptions{
+                       ConnectionId: 2,
+                       BoardId:      8,
+               },
+       }
+
+       // import raw data table
+       
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jira_api_projects.csv", 
"_raw_jira_api_projects")
+
+       // verify issue extraction
+       dataflowTester.FlushTabler(&models.JiraProject{})
+       dataflowTester.Subtask(tasks.ExtractProjectsMeta, taskData)
+       dataflowTester.VerifyTable(
+               models.JiraProject{},
+               "./snapshot_tables/_tool_jira_projects.csv",
+               []string{"connection_id", "id"},
+               []string{
+                       "connection_id",
+                       "id",
+                       "project_key",
+                       "name",
+                       "_raw_data_params",
+                       "_raw_data_table",
+                       "_raw_data_id",
+                       "_raw_data_remark",
+               },
+       )
+}
diff --git a/plugins/jira/e2e/raw_tables/_raw_jira_api_boards.csv 
b/plugins/jira/e2e/raw_tables/_raw_jira_api_boards.csv
new file mode 100644
index 00000000..99f53629
--- /dev/null
+++ b/plugins/jira/e2e/raw_tables/_raw_jira_api_boards.csv
@@ -0,0 +1,2 @@
+"id","params","data","url","input","created_at"
+7,"{""ConnectionId"":2,""BoardId"":8}","{""id"": 8, ""name"": ""迭代开发看板(停用)"", 
""self"": ""https://merico.atlassian.net/rest/agile/1.0/board/8"";, ""type"": 
""scrum"", ""location"": {""name"": ""Enterprise Edition (EE)"", ""avatarURI"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10552?size=small"";,
 ""projectId"": 10003, ""projectKey"": ""EE"", ""displayName"": ""Enterprise 
Edition (EE)"", ""projectName"": ""Enterprise Edition"", ""projectTypeKey"":  
[...]
diff --git a/plugins/jira/e2e/raw_tables/_raw_jira_api_issues.csv 
b/plugins/jira/e2e/raw_tables/_raw_jira_api_issues.csv
new file mode 100644
index 00000000..43386dea
--- /dev/null
+++ b/plugins/jira/e2e/raw_tables/_raw_jira_api_issues.csv
@@ -0,0 +1,31 @@
+"id","params","data","url","input","created_at"
+12441,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10063"", ""key"": 
""EE-1"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10063"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-1/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12442,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10064"", ""key"": 
""EE-2"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10064"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-2/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12443,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10065"", ""key"": 
""EE-3"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10065"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-3/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12444,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10066"", ""key"": 
""EE-4"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10066"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-4/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12445,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10067"", ""key"": 
""EE-5"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10067"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-5/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12446,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10068"", ""key"": 
""EE-6"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10068"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-6/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12447,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10070"", ""key"": 
""EE-8"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10070"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-8/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12448,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10071"", ""key"": 
""EE-9"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10071"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-9/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""https [...]
+12449,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10072"", ""key"": 
""EE-10"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10072"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-10/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12450,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10076"", ""key"": 
""EE-14"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10076"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-14/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12451,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10077"", ""key"": 
""EE-15"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10077"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-15/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12452,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10078"", ""key"": 
""EE-16"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10078"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-16/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12453,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10079"", ""key"": 
""EE-17"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10079"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-17/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12454,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10081"", ""key"": 
""EE-19"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10081"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-19/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12455,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10082"", ""key"": 
""EE-20"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10082"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-20/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12456,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10085"", ""key"": 
""EE-23"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10085"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-23/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12457,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10086"", ""key"": 
""EE-24"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10086"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-24/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""sprint"": null, ""status"": {""id"": 
""10068"", ""name"": ""已完成"", ""self"": ""htt [...]
+12458,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10087"", ""key"": 
""EE-25"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10087"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-25/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10063"", ""key"": 
""EE-1"", ""self"": ""https://merico.atlass [...]
+12459,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10088"", ""key"": 
""EE-26"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10088"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-26/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10063"", ""key"": 
""EE-1"", ""self"": ""https://merico.atlass [...]
+12460,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10089"", ""key"": 
""EE-27"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10089"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-27/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10063"", ""key"": 
""EE-1"", ""self"": ""https://merico.atlass [...]
+12461,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10090"", ""key"": 
""EE-28"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10090"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-28/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10063"", ""key"": 
""EE-1"", ""self"": ""https://merico.atlass [...]
+12462,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10091"", ""key"": 
""EE-29"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10091"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-29/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10064"", ""key"": 
""EE-2"", ""self"": ""https://merico.atlass [...]
+12463,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10092"", ""key"": 
""EE-30"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10092"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-30/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10064"", ""key"": 
""EE-2"", ""self"": ""https://merico.atlass [...]
+12464,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10093"", ""key"": 
""EE-31"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10093"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-31/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10064"", ""key"": 
""EE-2"", ""self"": ""https://merico.atlass [...]
+12465,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10094"", ""key"": 
""EE-32"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10094"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-32/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10064"", ""key"": 
""EE-2"", ""self"": ""https://merico.atlass [...]
+12466,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10095"", ""key"": 
""EE-33"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10095"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-33/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10063"", ""key"": 
""EE-1"", ""self"": ""https://merico.atlass [...]
+12467,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10096"", ""key"": 
""EE-34"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10096"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-34/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10065"", ""key"": 
""EE-3"", ""self"": ""https://merico.atlass [...]
+12468,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10097"", ""key"": 
""EE-35"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10097"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-35/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10065"", ""key"": 
""EE-3"", ""self"": ""https://merico.atlass [...]
+12469,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10098"", ""key"": 
""EE-36"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10098"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-36/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10065"", ""key"": 
""EE-3"", ""self"": ""https://merico.atlass [...]
+12470,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10099"", ""key"": 
""EE-37"", ""self"": 
""https://merico.atlassian.net/rest/agile/1.0/issue/10099"";, ""expand"": 
""operations,versionedRepresentations,editmeta,changelog,renderedFields"", 
""fields"": {""epic"": null, ""votes"": {""self"": 
""https://merico.atlassian.net/rest/api/2/issue/EE-37/votes"";, ""votes"": 0, 
""hasVoted"": false}, ""labels"": [], ""parent"": {""id"": ""10065"", ""key"": 
""EE-3"", ""self"": ""https://merico.atlass [...]
diff --git a/plugins/jira/e2e/raw_tables/_raw_jira_api_projects.csv 
b/plugins/jira/e2e/raw_tables/_raw_jira_api_projects.csv
new file mode 100644
index 00000000..d72a88a7
--- /dev/null
+++ b/plugins/jira/e2e/raw_tables/_raw_jira_api_projects.csv
@@ -0,0 +1,13 @@
+"id","params","data","url","input","created_at"
+48,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10007"", ""key"": ""CC"", 
""name"": ""Customer Center"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10007"";, ""style"": 
""classic"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""isPrivate"": false, ""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10417?size=xsmall"";,
 ""24x24"": ""https://merico.atlassian.net/rest/api/2/u [...]
+49,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10041"", ""key"": ""DEV"", 
""name"": ""DevLake"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10041"";, ""uuid"": 
""ffb92ace-fe5b-4883-bda5-c6e74bde0dcf"", ""style"": ""next-gen"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""entityId"": ""ffb92ace-fe5b-4883-bda5-c6e74bde0dcf"", ""isPrivate"": false, 
""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avata [...]
+50,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10033"", ""key"": ""DTM"", 
""name"": ""DevStream"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10033"";, ""uuid"": 
""e20b4ee7-8d71-45df-b353-5e732dd54bc2"", ""style"": ""next-gen"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""entityId"": ""e20b4ee7-8d71-45df-b353-5e732dd54bc2"", ""isPrivate"": false, 
""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_ava [...]
+51,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10038"", ""key"": ""DET"", 
""name"": ""dtm-e2e-test-do-not-delete"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10038"";, ""uuid"": 
""a09c574a-ee44-4cc6-961d-8fd389702b1d"", ""style"": ""next-gen"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""entityId"": ""a09c574a-ee44-4cc6-961d-8fd389702b1d"", ""isPrivate"": false, 
""avatarUrls"": {""16x16"": ""https://merico.atlassian.net/rest/ap [...]
+52,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10003"", ""key"": ""EE"", 
""name"": ""Enterprise Edition"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10003"";, ""style"": 
""classic"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""isPrivate"": false, ""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10552?size=xsmall"";,
 ""24x24"": ""https://merico.atlassian.net/rest/api/ [...]
+53,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10034"", ""key"": 
""HEAP"", ""name"": ""heapheap"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10034"";, ""uuid"": 
""40d71512-575f-4374-a08f-d19f2660c703"", ""style"": ""next-gen"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""entityId"": ""40d71512-575f-4374-a08f-d19f2660c703"", ""isPrivate"": true, 
""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avat [...]
+54,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10040"", ""key"": ""HEP"", 
""name"": ""HEP"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10040"";, ""uuid"": 
""69b8db3e-0e74-49b3-898e-5b744b7fb90a"", ""style"": ""next-gen"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""entityId"": ""69b8db3e-0e74-49b3-898e-5b744b7fb90a"", ""isPrivate"": true, 
""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/vie [...]
+55,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10008"", ""key"": ""NX"", 
""name"": ""Next"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10008"";, ""style"": 
""classic"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""isPrivate"": false, ""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10408?size=xsmall"";,
 ""24x24"": ""https://merico.atlassian.net/rest/api/2/universal_av [...]
+56,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10030"", ""key"": ""OR"", 
""name"": ""OSS-Ranking"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10030"";, ""style"": 
""classic"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""isPrivate"": false, ""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10417?size=xsmall"";,
 ""24x24"": ""https://merico.atlassian.net/rest/api/2/unive [...]
+57,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10037"", ""key"": 
""POTP"", ""name"": ""Product on Track Planning"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10037"";, ""style"": 
""classic"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""isPrivate"": false, ""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10416?size=xsmall"";,
 ""24x24"": ""https://merico.atlassian.net/ [...]
+58,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10022"", ""key"": 
""ROAD"", ""name"": ""Roadmap"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10022"";, ""uuid"": 
""9bcf1ed9-3b5d-4d92-9d8c-5d8e8290e0b0"", ""style"": ""next-gen"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""entityId"": ""9bcf1ed9-3b5d-4d92-9d8c-5d8e8290e0b0"", ""isPrivate"": false, 
""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avat [...]
+59,"{""ConnectionId"":2,""BoardId"":8}","{""id"": ""10029"", ""key"": 
""MERICO"", ""name"": ""思码逸跨团队协作"", ""self"": 
""https://merico.atlassian.net/rest/api/2/project/10029"";, ""style"": 
""classic"", ""expand"": 
""description,lead,issueTypes,url,projectKeys,permissions,insight"", 
""isPrivate"": false, ""avatarUrls"": {""16x16"": 
""https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10416?size=xsmall"";,
 ""24x24"": ""https://merico.atlassian.net/rest/api/2/univ [...]
diff --git a/plugins/jira/e2e/snapshot_tables/_tool_jira_board_issues.csv 
b/plugins/jira/e2e/snapshot_tables/_tool_jira_board_issues.csv
new file mode 100644
index 00000000..07e4cb49
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/_tool_jira_board_issues.csv
@@ -0,0 +1,31 @@
+connection_id,board_id,issue_id
+2,8,10063
+2,8,10064
+2,8,10065
+2,8,10066
+2,8,10067
+2,8,10068
+2,8,10070
+2,8,10071
+2,8,10072
+2,8,10076
+2,8,10077
+2,8,10078
+2,8,10079
+2,8,10081
+2,8,10082
+2,8,10085
+2,8,10086
+2,8,10087
+2,8,10088
+2,8,10089
+2,8,10090
+2,8,10091
+2,8,10092
+2,8,10093
+2,8,10094
+2,8,10095
+2,8,10096
+2,8,10097
+2,8,10098
+2,8,10099
diff --git a/plugins/jira/e2e/snapshot_tables/_tool_jira_boards.csv 
b/plugins/jira/e2e/snapshot_tables/_tool_jira_boards.csv
new file mode 100644
index 00000000..df40de57
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/_tool_jira_boards.csv
@@ -0,0 +1,2 @@
+connection_id,board_id,connection_id,board_id,project_id,name,self,type,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+2,8,2,8,10003,迭代开发看板(停用),https://merico.atlassian.net/rest/agile/1.0/board/8,scrum,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_boards,7,
diff --git a/plugins/jira/e2e/snapshot_tables/_tool_jira_issues.csv 
b/plugins/jira/e2e/snapshot_tables/_tool_jira_issues.csv
new file mode 100644
index 00000000..0d79b9dc
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/_tool_jira_issues.csv
@@ -0,0 +1,31 @@
+connection_id,issue_id,connection_id,issue_id,project_id,self,issue_key,summary,type,epic_key,status_name,status_key,story_point,original_estimate_minutes,aggregate_estimate_minutes,remaining_estimate_minutes,creator_account_id,creator_account_type,creator_display_name,assignee_account_id,assignee_account_type,assignee_display_name,priority_id,priority_name,parent_id,parent_key,sprint_id,sprint_name,resolution_date,created,updated,spent_minutes,lead_time_minutes,std_story_point,std_type,
 [...]
+2,10063,2,10063,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10063,EE-1,​四个排序图:测试/注释覆盖度、复用度、模块性,故事,,已完成,done,0,0,1260,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,0,,0,,2020-06-19T06:31:18.495+00:00,2020-06-12T00:13:13.360+00:00,2021-03-28T08:06:08.713+00:00,0,10458,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12441,,https://merico.atlassian.net/rest
 [...]
+2,10064,2,10064,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10064,EE-2,​问题堆叠分布排序图,故事,,已完成,done,0,0,840,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,0,,0,,2020-06-23T10:20:58.999+00:00,2020-06-12T00:15:36.123+00:00,2021-03-28T08:05:55.016+00:00,0,16445,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12442,,https://merico.atlassian.net/rest/api/2/univers
 [...]
+2,10065,2,10065,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10065,EE-3,​问题积压图率,故事,,已完成,done,0,0,600,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,0,,0,,2020-06-23T10:21:11.996+00:00,2020-06-12T00:15:41.600+00:00,2021-03-28T08:05:55.061+00:00,0,16445,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12443,,https://merico.atlassian.net/rest/api/2/universal_
 [...]
+2,10066,2,10066,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10066,EE-4,​问题分布的帕累托图,故事,,已完成,done,0,0,480,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,0,,0,,2020-06-23T10:21:23.562+00:00,2020-06-12T00:15:46.144+00:00,2021-03-28T08:06:09.535+00:00,0,16445,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12444,,https://merico.atlassian.net/rest/api/2/univers
 [...]
+2,10067,2,10067,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10067,EE-5,​通用技术任务,任务,,已完成,done,0,0,360,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,0,,0,,2020-06-18T04:06:00.747+00:00,2020-06-12T00:16:44.157+00:00,2021-03-28T08:05:54.622+00:00,0,8869,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12445,,https://merico.atlassian.net/rest/api/2/universal_a
 [...]
+2,10068,2,10068,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10068,EE-6,​变异系数、生产率的四象限图,故事,,已完成,done,0,0,1680,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c2490cf0c09e2e598,,Gerile 
Tu,3,Medium,0,,0,,2020-06-16T11:56:14.433+00:00,2020-06-12T00:17:26.986+00:00,2021-03-28T08:05:56.750+00:00,0,6458,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12446,,https://merico.atlassian.net/rest/api/2/universa
 [...]
+2,10070,2,10070,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10070,EE-8,​多团队支持,任务,,已完成,done,0,0,5400,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0ba04d9c0c220c18d8,,yanghui,3,Medium,0,,0,,2020-07-08T17:11:45.201+00:00,2020-06-12T00:18:58.050+00:00,2021-03-28T08:05:54.576+00:00,0,38452,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12447,,https://merico.atlassian.net/rest/api/2/universal_avatar/
 [...]
+2,10071,2,10071,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10071,EE-9,Common
 backend 拆分,任务,,已完成,done,0,1680,1680,1680,5e9711ba34f7b90c0fbc37d3,,Rankin 
Zheng,5ecfbd0ba04d9c0c220c18d8,,yanghui,3,Medium,0,,0,,2020-07-08T17:12:05.663+00:00,2020-06-12T00:19:17.336+00:00,2021-03-28T08:06:20.165+00:00,0,38452,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12448,,https://merico.atlassian.net/rest/api/2/
 [...]
+2,10072,2,10072,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10072,EE-10,​部署SaaS版本,任务,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0ba04d9c0c220c18d8,,yanghui,3,Medium,0,,0,,2020-07-08T17:11:55.247+00:00,2020-06-12T00:19:24.637+00:00,2021-03-28T08:05:54.472+00:00,0,38452,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12449,,https://merico.atlassian.net/rest/api/2/universal_avatar
 [...]
+2,10076,2,10076,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10076,EE-14,调整​文件夹结构,任务,,已完成,done,0,240,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,0,,0,,2020-06-15T08:59:51.304+00:00,2020-06-12T00:24:25.922+00:00,2021-03-28T08:05:56.152+00:00,600,4835,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12450,,https://merico.atlassian.net/rest/api/2/universal_avat
 [...]
+2,10077,2,10077,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10077,EE-15,路由权限控制,任务,,已完成,done,0,240,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,0,,0,,2020-06-15T09:00:26.956+00:00,2020-06-12T00:24:39.624+00:00,2021-03-28T08:06:01.995+00:00,480,4835,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12451,,https://merico.atlassian.net/rest/api/2/universal_avatar
 [...]
+2,10078,2,10078,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10078,EE-16,​优化前端
 webpack 开发阶段构建速度,任务,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,0,,0,,2020-06-15T09:01:44.159+00:00,2020-06-12T00:24:49.017+00:00,2021-03-28T08:05:55.863+00:00,60,4836,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12452,,https://merico.atlassian.net/rest/api/2/uni
 [...]
+2,10079,2,10079,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10079,EE-17,​新的错误处理机制,任务,,已完成,done,0,360,240,240,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,0,,0,,2020-07-22T07:25:29.104+00:00,2020-06-12T00:24:56.048+00:00,2021-03-28T08:05:54.426+00:00,120,58020,0,任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12453,,https://merico.atlassian.net/rest/api/2/universa
 [...]
+2,10081,2,10081,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10081,EE-19,​LDAP需要支持TLS和证书,故事,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0aaa47a00c1997ea8e,,chao.cheng,3,Medium,0,,0,,2020-06-18T08:34:11.117+00:00,2020-06-12T00:28:00.241+00:00,2021-03-28T08:05:57.326+00:00,0,9126,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12454,,https://merico.atlassian.net/rest/api/2/universa
 [...]
+2,10082,2,10082,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10082,EE-20,团队首页垂直化,故事,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0984083c0c12e5af8f,,lin.hao,3,Medium,0,,0,,2020-06-17T07:25:54.370+00:00,2020-06-12T00:29:43.677+00:00,2021-08-06T06:14:54.647+00:00,0,7616,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12455,,https://merico.atlassian.net/rest/api/2/universal_avatar/vi
 [...]
+2,10085,2,10085,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10085,EE-23,​批量删除事故,缺陷,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,0,,0,,2020-06-15T09:07:56.798+00:00,2020-06-12T00:33:57.204+00:00,2021-03-28T08:05:57.095+00:00,60,4833,0,缺陷,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12456,,https://merico.atlassian.net/rest/api/2/universal_avatar/v
 [...]
+2,10086,2,10086,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10086,EE-24,​LDAP支持自定义的证书上传,故事,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0aaa47a00c1997ea8e,,chao.cheng,3,Medium,0,,0,,2020-06-12T07:17:28.659+00:00,2020-06-12T00:35:15.489+00:00,2021-03-28T08:05:55.819+00:00,0,402,0,故事,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12457,,https://merico.atlassian.net/rest/api/2/universal
 [...]
+2,10087,2,10087,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10087,EE-25,​组件封装及Demo,子任务,,已完成,done,0,240,240,240,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,10063,EE-1,0,,2020-06-18T04:02:22.350+00:00,2020-06-12T00:40:54.210+00:00,2021-03-28T08:05:57.189+00:00,0,8841,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12458,,https://merico.atlassian.net/rest
 [...]
+2,10088,2,10088,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10088,EE-26,​定接口,子任务,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,10063,EE-1,0,,2020-06-15T09:06:51.438+00:00,2020-06-12T00:41:01.683+00:00,2021-03-28T08:05:55.208+00:00,0,4825,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12459,,https://merico.atlassian.net/rest/api/2/universal_av
 [...]
+2,10089,2,10089,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10089,EE-27,​提供后端接口,子任务,,已完成,done,0,660,660,660,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0ba04d9c0c220c18d8,,yanghui,3,Medium,10063,EE-1,0,,2020-06-19T06:31:31.662+00:00,2020-06-12T00:41:16.622+00:00,2021-03-28T08:05:55.498+00:00,0,10430,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12460,,https://merico.atlassian.net/rest/api/2/un
 [...]
+2,10090,2,10090,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10090,EE-28,​数据填充与联调,子任务,,已完成,done,0,360,360,360,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,10063,EE-1,0,,2020-06-18T04:03:04.637+00:00,2020-06-12T00:41:36.317+00:00,2021-03-28T08:06:05.443+00:00,0,8841,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12461,,https://merico.atlassian.net/rest/a
 [...]
+2,10091,2,10091,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10091,EE-29,​组件封装及Demo,子任务,,已完成,done,0,360,360,360,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,10064,EE-2,0,,2020-06-18T04:03:30.760+00:00,2020-06-12T00:48:29.035+00:00,2021-03-28T08:05:55.731+00:00,0,8835,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12462,,https://merico.atlassian.net/rest
 [...]
+2,10092,2,10092,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10092,EE-30,​定接口,子任务,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,10064,EE-2,0,,2020-06-15T09:06:40.206+00:00,2020-06-12T00:48:39.803+00:00,2021-03-28T08:05:55.159+00:00,0,4818,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12463,,https://merico.atlassian.net/rest/api/2/universal_av
 [...]
+2,10093,2,10093,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10093,EE-31,​后端接口,子任务,,已完成,done,0,120,120,120,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0ba04d9c0c220c18d8,,yanghui,3,Medium,10064,EE-2,0,,2020-06-19T07:35:31.762+00:00,2020-06-12T00:48:46.751+00:00,2021-03-28T08:05:55.544+00:00,0,10486,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12464,,https://merico.atlassian.net/rest/api/2/univ
 [...]
+2,10094,2,10094,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10094,EE-32,​数据填充与联调,子任务,,已完成,done,0,360,360,360,5e9711ba34f7b90c0fbc37d3,,Rankin
 Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,10064,EE-2,0,,2020-06-18T04:03:48.818+00:00,2020-06-12T00:48:53.279+00:00,2021-03-28T08:05:55.592+00:00,0,8834,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12465,,https://merico.atlassian.net/rest/a
 [...]
+2,10095,2,10095,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10095,EE-33,准备测试用例,子任务,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0984083c0c12e5af8f,,lin.hao,3,Medium,10063,EE-1,0,,2020-06-19T06:32:19.340+00:00,2020-06-12T00:50:45.674+00:00,2021-03-28T08:05:55.253+00:00,0,10421,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12466,,https://merico.atlassian.net/rest/api/2/universal
 [...]
+2,10096,2,10096,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10096,EE-34,​
 组件封装及Demo,子任务,,已完成,done,0,240,360,360,5e9711ba34f7b90c0fbc37d3,,Rankin 
Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,10065,EE-3,0,,2020-06-18T04:04:05.951+00:00,2020-06-12T00:51:57.807+00:00,2021-03-28T08:05:57.232+00:00,0,8832,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12467,,https://merico.atlassian.net/res
 [...]
+2,10097,2,10097,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10097,EE-35,定接口,子任务,,已完成,done,0,0,120,120,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0a47d31e0c2a15fd87,,yuxiang,3,Medium,10065,EE-3,0,,2020-06-15T09:06:30.942+00:00,2020-06-12T00:52:04.767+00:00,2021-03-28T08:05:55.353+00:00,0,4814,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12468,,https://merico.atlassian.net/rest/api/2/universal
 [...]
+2,10098,2,10098,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10098,EE-36,后端接口,子任务,,已完成,done,0,120,120,120,5e9711ba34f7b90c0fbc37d3,,Rankin
 
Zheng,5ecfbd0ba04d9c0c220c18d8,,yanghui,3,Medium,10065,EE-3,0,,2020-06-19T07:35:44.696+00:00,2020-06-12T00:52:12.678+00:00,2021-03-28T08:05:55.685+00:00,0,10483,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12469,,https://merico.atlassian.net/rest/api/2/unive
 [...]
+2,10099,2,10099,10003,https://merico.atlassian.net/rest/agile/1.0/issue/10099,EE-37,数据填充与联调
 ,子任务,,已完成,done,0,0,0,0,5e9711ba34f7b90c0fbc37d3,,Rankin 
Zheng,5ecfbd0c730ec90c1999cadf,,Dingding 
Zhang,3,Medium,10065,EE-3,0,,2020-06-18T04:04:31.261+00:00,2020-06-12T00:52:23.456+00:00,2021-03-28T08:05:55.638+00:00,0,8832,0,子任务,DONE,2022-06-23T12:50:47.748+00:00,2022-06-23T12:50:47.748+00:00,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_issues,12470,,https://merico.atlassian.net/rest/api/2/u
 [...]
diff --git a/plugins/jira/e2e/snapshot_tables/_tool_jira_projects.csv 
b/plugins/jira/e2e/snapshot_tables/_tool_jira_projects.csv
new file mode 100644
index 00000000..c21bdfa5
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/_tool_jira_projects.csv
@@ -0,0 +1,13 @@
+connection_id,id,connection_id,id,project_key,name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+2,10003,2,10003,EE,Enterprise 
Edition,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,52,
+2,10007,2,10007,CC,Customer 
Center,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,48,
+2,10008,2,10008,NX,Next,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,55,
+2,10022,2,10022,ROAD,Roadmap,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,58,
+2,10029,2,10029,MERICO,思码逸跨团队协作,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,59,
+2,10030,2,10030,OR,OSS-Ranking,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,56,
+2,10033,2,10033,DTM,DevStream,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,50,
+2,10034,2,10034,HEAP,heapheap,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,53,
+2,10037,2,10037,POTP,Product on Track 
Planning,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,57,
+2,10038,2,10038,DET,dtm-e2e-test-do-not-delete,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,51,
+2,10040,2,10040,HEP,HEP,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,54,
+2,10041,2,10041,DEV,DevLake,"{""ConnectionId"":2,""BoardId"":8}",_raw_jira_api_projects,49,
diff --git a/plugins/jira/e2e/snapshot_tables/board_issues.csv 
b/plugins/jira/e2e/snapshot_tables/board_issues.csv
new file mode 100644
index 00000000..79e10b4b
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/board_issues.csv
@@ -0,0 +1,31 @@
+board_id,issue_id
+jira:JiraBoard:2:8,jira:JiraIssue:2:10063
+jira:JiraBoard:2:8,jira:JiraIssue:2:10064
+jira:JiraBoard:2:8,jira:JiraIssue:2:10065
+jira:JiraBoard:2:8,jira:JiraIssue:2:10066
+jira:JiraBoard:2:8,jira:JiraIssue:2:10067
+jira:JiraBoard:2:8,jira:JiraIssue:2:10068
+jira:JiraBoard:2:8,jira:JiraIssue:2:10070
+jira:JiraBoard:2:8,jira:JiraIssue:2:10071
+jira:JiraBoard:2:8,jira:JiraIssue:2:10072
+jira:JiraBoard:2:8,jira:JiraIssue:2:10076
+jira:JiraBoard:2:8,jira:JiraIssue:2:10077
+jira:JiraBoard:2:8,jira:JiraIssue:2:10078
+jira:JiraBoard:2:8,jira:JiraIssue:2:10079
+jira:JiraBoard:2:8,jira:JiraIssue:2:10081
+jira:JiraBoard:2:8,jira:JiraIssue:2:10082
+jira:JiraBoard:2:8,jira:JiraIssue:2:10085
+jira:JiraBoard:2:8,jira:JiraIssue:2:10086
+jira:JiraBoard:2:8,jira:JiraIssue:2:10087
+jira:JiraBoard:2:8,jira:JiraIssue:2:10088
+jira:JiraBoard:2:8,jira:JiraIssue:2:10089
+jira:JiraBoard:2:8,jira:JiraIssue:2:10090
+jira:JiraBoard:2:8,jira:JiraIssue:2:10091
+jira:JiraBoard:2:8,jira:JiraIssue:2:10092
+jira:JiraBoard:2:8,jira:JiraIssue:2:10093
+jira:JiraBoard:2:8,jira:JiraIssue:2:10094
+jira:JiraBoard:2:8,jira:JiraIssue:2:10095
+jira:JiraBoard:2:8,jira:JiraIssue:2:10096
+jira:JiraBoard:2:8,jira:JiraIssue:2:10097
+jira:JiraBoard:2:8,jira:JiraIssue:2:10098
+jira:JiraBoard:2:8,jira:JiraIssue:2:10099
diff --git a/plugins/jira/e2e/snapshot_tables/boards.csv 
b/plugins/jira/e2e/snapshot_tables/boards.csv
new file mode 100644
index 00000000..80891045
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/boards.csv
@@ -0,0 +1,2 @@
+id,id,name,description,url,created_date
+jira:JiraBoard:2:8,jira:JiraBoard:2:8,迭代开发看板(停用),,https://merico.atlassian.net/rest/agile/1.0/board/8,
diff --git a/plugins/jira/e2e/snapshot_tables/issues.csv 
b/plugins/jira/e2e/snapshot_tables/issues.csv
new file mode 100644
index 00000000..fc50b434
--- /dev/null
+++ b/plugins/jira/e2e/snapshot_tables/issues.csv
@@ -0,0 +1,31 @@
+id,url,icon_url,issue_key,title,description,epic_key,type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
+jira:JiraIssue:2:10063,https://merico.atlassian.net/browse/EE-1,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-1,​四个排序图:测试/注释覆盖度、复用度、模块性,,,故事,DONE,已完成,0,2020-06-19T06:31:18.495+00:00,2020-06-12T00:13:13.360+00:00,2021-03-28T08:06:08.713+00:00,10458,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10064,https://merico.atlassian.net/browse/EE-2,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-2,​问题堆叠分布排序图,,,故事,DONE,已完成,0,2020-06-23T10:20:58.999+00:00,2020-06-12T00:15:36.123+00:00,2021-03-28T08:05:55.016+00:00,16445,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10065,https://merico.atlassian.net/browse/EE-3,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-3,​问题积压图率,,,故事,DONE,已完成,0,2020-06-23T10:21:11.996+00:00,2020-06-12T00:15:41.600+00:00,2021-03-28T08:05:55.061+00:00,16445,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10066,https://merico.atlassian.net/browse/EE-4,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-4,​问题分布的帕累托图,,,故事,DONE,已完成,0,2020-06-23T10:21:23.562+00:00,2020-06-12T00:15:46.144+00:00,2021-03-28T08:06:09.535+00:00,16445,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10067,https://merico.atlassian.net/browse/EE-5,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-5,​通用技术任务,,,任务,DONE,已完成,0,2020-06-18T04:06:00.747+00:00,2020-06-12T00:16:44.157+00:00,2021-03-28T08:05:54.622+00:00,8869,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10068,https://merico.atlassian.net/browse/EE-6,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-6,​变异系数、生产率的四象限图,,,故事,DONE,已完成,0,2020-06-16T11:56:14.433+00:00,2020-06-12T00:17:26.986+00:00,2021-03-28T08:05:56.750+00:00,6458,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c2490cf0c09e2e598,Gerile Tu,,
+jira:JiraIssue:2:10070,https://merico.atlassian.net/browse/EE-8,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-8,​多团队支持,,,任务,DONE,已完成,0,2020-07-08T17:11:45.201+00:00,2020-06-12T00:18:58.050+00:00,2021-03-28T08:05:54.576+00:00,38452,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0ba04d9c0c220c18d8,yanghui,,
+jira:JiraIssue:2:10071,https://merico.atlassian.net/browse/EE-9,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-9,Common
 backend 
拆分,,,任务,DONE,已完成,0,2020-07-08T17:12:05.663+00:00,2020-06-12T00:19:17.336+00:00,2021-03-28T08:06:20.165+00:00,38452,,Medium,1680,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0ba04d9c0c220c18d8,yanghui,,
+jira:JiraIssue:2:10072,https://merico.atlassian.net/browse/EE-10,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-10,​部署SaaS版本,,,任务,DONE,已完成,0,2020-07-08T17:11:55.247+00:00,2020-06-12T00:19:24.637+00:00,2021-03-28T08:05:54.472+00:00,38452,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0ba04d9c0c220c18d8,yanghui,,
+jira:JiraIssue:2:10076,https://merico.atlassian.net/browse/EE-14,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-14,调整​文件夹结构,,,任务,DONE,已完成,0,2020-06-15T08:59:51.304+00:00,2020-06-12T00:24:25.922+00:00,2021-03-28T08:05:56.152+00:00,4835,,Medium,240,600,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10077,https://merico.atlassian.net/browse/EE-15,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-15,路由权限控制,,,任务,DONE,已完成,0,2020-06-15T09:00:26.956+00:00,2020-06-12T00:24:39.624+00:00,2021-03-28T08:06:01.995+00:00,4835,,Medium,240,480,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10078,https://merico.atlassian.net/browse/EE-16,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-16,​优化前端
 webpack 
开发阶段构建速度,,,任务,DONE,已完成,0,2020-06-15T09:01:44.159+00:00,2020-06-12T00:24:49.017+00:00,2021-03-28T08:05:55.863+00:00,4836,,Medium,0,60,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10079,https://merico.atlassian.net/browse/EE-17,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10318?size=medium,EE-17,​新的错误处理机制,,,任务,DONE,已完成,0,2020-07-22T07:25:29.104+00:00,2020-06-12T00:24:56.048+00:00,2021-03-28T08:05:54.426+00:00,58020,,Medium,360,120,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10081,https://merico.atlassian.net/browse/EE-19,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-19,​LDAP需要支持TLS和证书,,,故事,DONE,已完成,0,2020-06-18T08:34:11.117+00:00,2020-06-12T00:28:00.241+00:00,2021-03-28T08:05:57.326+00:00,9126,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0aaa47a00c1997ea8e,chao.cheng,,
+jira:JiraIssue:2:10082,https://merico.atlassian.net/browse/EE-20,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-20,团队首页垂直化,,,故事,DONE,已完成,0,2020-06-17T07:25:54.370+00:00,2020-06-12T00:29:43.677+00:00,2021-08-06T06:14:54.647+00:00,7616,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0984083c0c12e5af8f,lin.hao,,
+jira:JiraIssue:2:10085,https://merico.atlassian.net/browse/EE-23,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10303?size=medium,EE-23,​批量删除事故,,,缺陷,DONE,已完成,0,2020-06-15T09:07:56.798+00:00,2020-06-12T00:33:57.204+00:00,2021-03-28T08:05:57.095+00:00,4833,,Medium,0,60,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10086,https://merico.atlassian.net/browse/EE-24,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10315?size=medium,EE-24,​LDAP支持自定义的证书上传,,,故事,DONE,已完成,0,2020-06-12T07:17:28.659+00:00,2020-06-12T00:35:15.489+00:00,2021-03-28T08:05:55.819+00:00,402,,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0aaa47a00c1997ea8e,chao.cheng,,
+jira:JiraIssue:2:10087,https://merico.atlassian.net/browse/EE-25,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-25,​组件封装及Demo,,,子任务,DONE,已完成,0,2020-06-18T04:02:22.350+00:00,2020-06-12T00:40:54.210+00:00,2021-03-28T08:05:57.189+00:00,8841,jira:JiraIssue:2:10063,Medium,240,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10088,https://merico.atlassian.net/browse/EE-26,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-26,​定接口,,,子任务,DONE,已完成,0,2020-06-15T09:06:51.438+00:00,2020-06-12T00:41:01.683+00:00,2021-03-28T08:05:55.208+00:00,4825,jira:JiraIssue:2:10063,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10089,https://merico.atlassian.net/browse/EE-27,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-27,​提供后端接口,,,子任务,DONE,已完成,0,2020-06-19T06:31:31.662+00:00,2020-06-12T00:41:16.622+00:00,2021-03-28T08:05:55.498+00:00,10430,jira:JiraIssue:2:10063,Medium,660,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0ba04d9c0c220c18d8,yanghui,,
+jira:JiraIssue:2:10090,https://merico.atlassian.net/browse/EE-28,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-28,​数据填充与联调,,,子任务,DONE,已完成,0,2020-06-18T04:03:04.637+00:00,2020-06-12T00:41:36.317+00:00,2021-03-28T08:06:05.443+00:00,8841,jira:JiraIssue:2:10063,Medium,360,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10091,https://merico.atlassian.net/browse/EE-29,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-29,​组件封装及Demo,,,子任务,DONE,已完成,0,2020-06-18T04:03:30.760+00:00,2020-06-12T00:48:29.035+00:00,2021-03-28T08:05:55.731+00:00,8835,jira:JiraIssue:2:10064,Medium,360,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10092,https://merico.atlassian.net/browse/EE-30,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-30,​定接口,,,子任务,DONE,已完成,0,2020-06-15T09:06:40.206+00:00,2020-06-12T00:48:39.803+00:00,2021-03-28T08:05:55.159+00:00,4818,jira:JiraIssue:2:10064,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10093,https://merico.atlassian.net/browse/EE-31,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-31,​后端接口,,,子任务,DONE,已完成,0,2020-06-19T07:35:31.762+00:00,2020-06-12T00:48:46.751+00:00,2021-03-28T08:05:55.544+00:00,10486,jira:JiraIssue:2:10064,Medium,120,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0ba04d9c0c220c18d8,yanghui,,
+jira:JiraIssue:2:10094,https://merico.atlassian.net/browse/EE-32,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-32,​数据填充与联调,,,子任务,DONE,已完成,0,2020-06-18T04:03:48.818+00:00,2020-06-12T00:48:53.279+00:00,2021-03-28T08:05:55.592+00:00,8834,jira:JiraIssue:2:10064,Medium,360,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10095,https://merico.atlassian.net/browse/EE-33,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-33,准备测试用例,,,子任务,DONE,已完成,0,2020-06-19T06:32:19.340+00:00,2020-06-12T00:50:45.674+00:00,2021-03-28T08:05:55.253+00:00,10421,jira:JiraIssue:2:10063,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0984083c0c12e5af8f,lin.hao,,
+jira:JiraIssue:2:10096,https://merico.atlassian.net/browse/EE-34,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-34,​
 
组件封装及Demo,,,子任务,DONE,已完成,0,2020-06-18T04:04:05.951+00:00,2020-06-12T00:51:57.807+00:00,2021-03-28T08:05:57.232+00:00,8832,jira:JiraIssue:2:10065,Medium,240,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
+jira:JiraIssue:2:10097,https://merico.atlassian.net/browse/EE-35,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-35,定接口,,,子任务,DONE,已完成,0,2020-06-15T09:06:30.942+00:00,2020-06-12T00:52:04.767+00:00,2021-03-28T08:05:55.353+00:00,4814,jira:JiraIssue:2:10065,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0a47d31e0c2a15fd87,yuxiang,,
+jira:JiraIssue:2:10098,https://merico.atlassian.net/browse/EE-36,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-36,后端接口,,,子任务,DONE,已完成,0,2020-06-19T07:35:44.696+00:00,2020-06-12T00:52:12.678+00:00,2021-03-28T08:05:55.685+00:00,10483,jira:JiraIssue:2:10065,Medium,120,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0ba04d9c0c220c18d8,yanghui,,
+jira:JiraIssue:2:10099,https://merico.atlassian.net/browse/EE-37,https://merico.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium,EE-37,数据填充与联调
 
,,,子任务,DONE,已完成,0,2020-06-18T04:04:31.261+00:00,2020-06-12T00:52:23.456+00:00,2021-03-28T08:05:55.638+00:00,8832,jira:JiraIssue:2:10065,Medium,0,0,0,jira:JiraUser:2:5e9711ba34f7b90c0fbc37d3,Rankin
 Zheng,jira:JiraUser:2:5ecfbd0c730ec90c1999cadf,Dingding Zhang,,
diff --git a/plugins/jira/jira.go b/plugins/jira/impl/impl.go
similarity index 52%
copy from plugins/jira/jira.go
copy to plugins/jira/impl/impl.go
index 97abb9d8..582b29a8 100644
--- a/plugins/jira/jira.go
+++ b/plugins/jira/impl/impl.go
@@ -15,7 +15,7 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package main // must be main for plugin entry point
+package impl
 
 import (
        "fmt"
@@ -29,9 +29,7 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
        
"github.com/apache/incubator-devlake/plugins/jira/models/migrationscripts"
        "github.com/apache/incubator-devlake/plugins/jira/tasks"
-       "github.com/apache/incubator-devlake/runner"
        "github.com/mitchellh/mapstructure"
-       "github.com/spf13/cobra"
        "github.com/spf13/viper"
        "gorm.io/gorm"
 )
@@ -55,48 +53,48 @@ func (plugin Jira) Description() string {
 
 func (plugin Jira) SubTaskMetas() []core.SubTaskMeta {
        return []core.SubTaskMeta{
-               {Name: "collectStatus", EntryPoint: tasks.CollectStatus, 
EnabledByDefault: true, Description: "collect Jira status"},
-               {Name: "extractStatus", EntryPoint: tasks.ExtractStatus, 
EnabledByDefault: true, Description: "extract Jira status"},
+               tasks.CollectStatusMeta,
+               tasks.ExtractStatusMeta,
 
-               {Name: "collectProjects", EntryPoint: tasks.CollectProjects, 
EnabledByDefault: true, Description: "collect Jira projects"},
-               {Name: "extractProjects", EntryPoint: tasks.ExtractProjects, 
EnabledByDefault: true, Description: "extract Jira projects"},
+               tasks.CollectProjectsMeta,
+               tasks.ExtractProjectsMeta,
 
-               {Name: "collectBoard", EntryPoint: tasks.CollectBoard, 
EnabledByDefault: true, Description: "collect Jira board"},
-               {Name: "extractBoard", EntryPoint: tasks.ExtractBoard, 
EnabledByDefault: true, Description: "extract Jira board"},
+               tasks.CollectBoardMeta,
+               tasks.ExtractBoardMeta,
 
-               {Name: "collectIssues", EntryPoint: tasks.CollectIssues, 
EnabledByDefault: true, Description: "collect Jira issues"},
-               {Name: "extractIssues", EntryPoint: tasks.ExtractIssues, 
EnabledByDefault: true, Description: "extract Jira issues"},
+               tasks.CollectIssuesMeta,
+               tasks.ExtractIssuesMeta,
 
-               {Name: "collectChangelogs", EntryPoint: 
tasks.CollectChangelogs, EnabledByDefault: true, Description: "collect Jira 
change logs"},
-               {Name: "extractChangelogs", EntryPoint: 
tasks.ExtractChangelogs, EnabledByDefault: true, Description: "extract Jira 
change logs"},
+               tasks.CollectChangelogsMeta,
+               tasks.ExtractChangelogsMeta,
 
-               {Name: "collectUsers", EntryPoint: tasks.CollectUsers, 
EnabledByDefault: true, Description: "collect Jira users"},
+               tasks.CollectUsersMeta,
 
-               {Name: "collectWorklogs", EntryPoint: tasks.CollectWorklogs, 
EnabledByDefault: true, Description: "collect Jira work logs"},
-               {Name: "extractWorklogs", EntryPoint: tasks.ExtractWorklogs, 
EnabledByDefault: true, Description: "extract Jira work logs"},
+               tasks.CollectWorklogsMeta,
+               tasks.ExtractWorklogsMeta,
 
-               {Name: "collectRemotelinks", EntryPoint: 
tasks.CollectRemotelinks, EnabledByDefault: true, Description: "collect Jira 
remote links"},
-               {Name: "extractRemotelinks", EntryPoint: 
tasks.ExtractRemotelinks, EnabledByDefault: true, Description: "extract Jira 
remote links"},
+               tasks.CollectRemotelinksMeta,
+               tasks.ExtractRemotelinksMeta,
 
-               {Name: "collectSprints", EntryPoint: tasks.CollectSprints, 
EnabledByDefault: true, Description: "collect Jira sprints"},
-               {Name: "extractSprints", EntryPoint: tasks.ExtractSprints, 
EnabledByDefault: true, Description: "extract Jira sprints"},
+               tasks.CollectSprintsMeta,
+               tasks.ExtractSprintsMeta,
 
-               {Name: "convertBoard", EntryPoint: tasks.ConvertBoard, 
EnabledByDefault: true, Description: "convert Jira board"},
+               tasks.ConvertBoardMeta,
 
-               {Name: "convertIssues", EntryPoint: tasks.ConvertIssues, 
EnabledByDefault: true, Description: "convert Jira issues"},
+               tasks.ConvertIssuesMeta,
 
-               {Name: "convertWorklogs", EntryPoint: tasks.ConvertWorklogs, 
EnabledByDefault: true, Description: "convert Jira work logs"},
+               tasks.ConvertWorklogsMeta,
 
-               {Name: "convertChangelogs", EntryPoint: 
tasks.ConvertChangelogs, EnabledByDefault: true, Description: "convert Jira 
change logs"},
+               tasks.ConvertChangelogsMeta,
 
-               {Name: "convertSprints", EntryPoint: tasks.ConvertSprints, 
EnabledByDefault: true, Description: "convert Jira sprints"},
-               {Name: "convertSprintIssues", EntryPoint: 
tasks.ConvertSprintIssues, EnabledByDefault: true, Description: "convert Jira 
sprint_issues"},
+               tasks.ConvertSprintsMeta,
+               tasks.ConvertSprintIssuesMeta,
 
-               {Name: "convertIssueCommits", EntryPoint: 
tasks.ConvertIssueCommits, EnabledByDefault: true, Description: "convert Jira 
issue commits"},
-               {Name: "convertIssueRepoCommits", EntryPoint: 
tasks.ConvertIssueRepoCommits, EnabledByDefault: false, Description: "convert 
Jira issue repo commits"},
+               tasks.ConvertIssueCommitsMeta,
+               tasks.ConvertIssueRepoCommitsMeta,
 
-               {Name: "extractUsers", EntryPoint: tasks.ExtractUsers, 
EnabledByDefault: true, Description: "extract Jira users"},
-               {Name: "convertUsers", EntryPoint: tasks.ConvertUsers, 
EnabledByDefault: true, Description: "convert Jira users"},
+               tasks.ExtractUsersMeta,
+               tasks.ConvertUsersMeta,
        }
 }
 
@@ -198,24 +196,3 @@ func (plugin Jira) ApiResources() 
map[string]map[string]core.ApiResourceHandler
                },
        }
 }
-
-// Export a variable named PluginEntry for Framework to search and load
-var PluginEntry Jira //nolint
-
-// standalone mode for debugging
-func main() {
-       cmd := &cobra.Command{Use: "jira"}
-       connectionId := cmd.Flags().Uint64P("connection", "c", 0, "jira 
connection id")
-       boardId := cmd.Flags().Uint64P("board", "b", 0, "jira board id")
-       _ = cmd.MarkFlagRequired("connection")
-       _ = cmd.MarkFlagRequired("board")
-       since := cmd.Flags().StringP("since", "s", "", "collect data that are 
updated after specified time, ie 2006-05-06T07:08:09Z")
-       cmd.Run = func(c *cobra.Command, args []string) {
-               runner.DirectRun(c, args, PluginEntry, map[string]interface{}{
-                       "connectionId": *connectionId,
-                       "boardId":      *boardId,
-                       "since":        *since,
-               })
-       }
-       runner.RunCmd(cmd)
-}
diff --git a/plugins/jira/jira.go b/plugins/jira/jira.go
index 97abb9d8..0cf85de0 100644
--- a/plugins/jira/jira.go
+++ b/plugins/jira/jira.go
@@ -18,189 +18,12 @@ limitations under the License.
 package main // must be main for plugin entry point
 
 import (
-       "fmt"
-       "net/http"
-       "time"
-
-       "github.com/apache/incubator-devlake/migration"
-       "github.com/apache/incubator-devlake/plugins/core"
-       "github.com/apache/incubator-devlake/plugins/helper"
-       "github.com/apache/incubator-devlake/plugins/jira/api"
-       "github.com/apache/incubator-devlake/plugins/jira/models"
-       
"github.com/apache/incubator-devlake/plugins/jira/models/migrationscripts"
-       "github.com/apache/incubator-devlake/plugins/jira/tasks"
+       "github.com/apache/incubator-devlake/plugins/jira/impl"
        "github.com/apache/incubator-devlake/runner"
-       "github.com/mitchellh/mapstructure"
        "github.com/spf13/cobra"
-       "github.com/spf13/viper"
-       "gorm.io/gorm"
 )
 
-var _ core.PluginMeta = (*Jira)(nil)
-var _ core.PluginInit = (*Jira)(nil)
-var _ core.PluginTask = (*Jira)(nil)
-var _ core.PluginApi = (*Jira)(nil)
-var _ core.Migratable = (*Jira)(nil)
-
-type Jira struct{}
-
-func (plugin Jira) Init(config *viper.Viper, logger core.Logger, db *gorm.DB) 
error {
-       api.Init(config, logger, db)
-       return nil
-}
-
-func (plugin Jira) Description() string {
-       return "To collect and enrich data from JIRA"
-}
-
-func (plugin Jira) SubTaskMetas() []core.SubTaskMeta {
-       return []core.SubTaskMeta{
-               {Name: "collectStatus", EntryPoint: tasks.CollectStatus, 
EnabledByDefault: true, Description: "collect Jira status"},
-               {Name: "extractStatus", EntryPoint: tasks.ExtractStatus, 
EnabledByDefault: true, Description: "extract Jira status"},
-
-               {Name: "collectProjects", EntryPoint: tasks.CollectProjects, 
EnabledByDefault: true, Description: "collect Jira projects"},
-               {Name: "extractProjects", EntryPoint: tasks.ExtractProjects, 
EnabledByDefault: true, Description: "extract Jira projects"},
-
-               {Name: "collectBoard", EntryPoint: tasks.CollectBoard, 
EnabledByDefault: true, Description: "collect Jira board"},
-               {Name: "extractBoard", EntryPoint: tasks.ExtractBoard, 
EnabledByDefault: true, Description: "extract Jira board"},
-
-               {Name: "collectIssues", EntryPoint: tasks.CollectIssues, 
EnabledByDefault: true, Description: "collect Jira issues"},
-               {Name: "extractIssues", EntryPoint: tasks.ExtractIssues, 
EnabledByDefault: true, Description: "extract Jira issues"},
-
-               {Name: "collectChangelogs", EntryPoint: 
tasks.CollectChangelogs, EnabledByDefault: true, Description: "collect Jira 
change logs"},
-               {Name: "extractChangelogs", EntryPoint: 
tasks.ExtractChangelogs, EnabledByDefault: true, Description: "extract Jira 
change logs"},
-
-               {Name: "collectUsers", EntryPoint: tasks.CollectUsers, 
EnabledByDefault: true, Description: "collect Jira users"},
-
-               {Name: "collectWorklogs", EntryPoint: tasks.CollectWorklogs, 
EnabledByDefault: true, Description: "collect Jira work logs"},
-               {Name: "extractWorklogs", EntryPoint: tasks.ExtractWorklogs, 
EnabledByDefault: true, Description: "extract Jira work logs"},
-
-               {Name: "collectRemotelinks", EntryPoint: 
tasks.CollectRemotelinks, EnabledByDefault: true, Description: "collect Jira 
remote links"},
-               {Name: "extractRemotelinks", EntryPoint: 
tasks.ExtractRemotelinks, EnabledByDefault: true, Description: "extract Jira 
remote links"},
-
-               {Name: "collectSprints", EntryPoint: tasks.CollectSprints, 
EnabledByDefault: true, Description: "collect Jira sprints"},
-               {Name: "extractSprints", EntryPoint: tasks.ExtractSprints, 
EnabledByDefault: true, Description: "extract Jira sprints"},
-
-               {Name: "convertBoard", EntryPoint: tasks.ConvertBoard, 
EnabledByDefault: true, Description: "convert Jira board"},
-
-               {Name: "convertIssues", EntryPoint: tasks.ConvertIssues, 
EnabledByDefault: true, Description: "convert Jira issues"},
-
-               {Name: "convertWorklogs", EntryPoint: tasks.ConvertWorklogs, 
EnabledByDefault: true, Description: "convert Jira work logs"},
-
-               {Name: "convertChangelogs", EntryPoint: 
tasks.ConvertChangelogs, EnabledByDefault: true, Description: "convert Jira 
change logs"},
-
-               {Name: "convertSprints", EntryPoint: tasks.ConvertSprints, 
EnabledByDefault: true, Description: "convert Jira sprints"},
-               {Name: "convertSprintIssues", EntryPoint: 
tasks.ConvertSprintIssues, EnabledByDefault: true, Description: "convert Jira 
sprint_issues"},
-
-               {Name: "convertIssueCommits", EntryPoint: 
tasks.ConvertIssueCommits, EnabledByDefault: true, Description: "convert Jira 
issue commits"},
-               {Name: "convertIssueRepoCommits", EntryPoint: 
tasks.ConvertIssueRepoCommits, EnabledByDefault: false, Description: "convert 
Jira issue repo commits"},
-
-               {Name: "extractUsers", EntryPoint: tasks.ExtractUsers, 
EnabledByDefault: true, Description: "extract Jira users"},
-               {Name: "convertUsers", EntryPoint: tasks.ConvertUsers, 
EnabledByDefault: true, Description: "convert Jira users"},
-       }
-}
-
-func (plugin Jira) PrepareTaskData(taskCtx core.TaskContext, options 
map[string]interface{}) (interface{}, error) {
-       var op tasks.JiraOptions
-       var err error
-       logger := taskCtx.GetLogger()
-       logger.Debug("%v", options)
-       err = mapstructure.Decode(options, &op)
-       if err != nil {
-               return nil, err
-       }
-       if op.ConnectionId == 0 {
-               return nil, fmt.Errorf("connectionId is invalid")
-       }
-       connection := &models.JiraConnection{}
-       connectionHelper := helper.NewConnectionHelper(
-               taskCtx,
-               nil,
-       )
-       if err != nil {
-               return nil, err
-       }
-       err = connectionHelper.FirstById(connection, op.ConnectionId)
-       if err != nil {
-               return nil, err
-       }
-
-       var since time.Time
-       if op.Since != "" {
-               since, err = time.Parse("2006-01-02T15:04:05Z", op.Since)
-               if err != nil {
-                       return nil, fmt.Errorf("invalid value for `since`: %w", 
err)
-               }
-       }
-       jiraApiClient, err := tasks.NewJiraApiClient(taskCtx, connection)
-       if err != nil {
-               return nil, fmt.Errorf("failed to create jira api client: %w", 
err)
-       }
-       info, code, err := tasks.GetJiraServerInfo(jiraApiClient)
-       if err != nil || code != http.StatusOK || info == nil {
-               return nil, fmt.Errorf("fail to get server info: error:[%s] 
code:[%d]", err, code)
-       }
-       taskData := &tasks.JiraTaskData{
-               Options:        &op,
-               ApiClient:      jiraApiClient,
-               Connection:     connection,
-               JiraServerInfo: *info,
-       }
-       if !since.IsZero() {
-               taskData.Since = &since
-               logger.Debug("collect data updated since %s", since)
-       }
-       return taskData, nil
-}
-
-func (plugin Jira) RootPkgPath() string {
-       return "github.com/apache/incubator-devlake/plugins/jira"
-}
-
-func (plugin Jira) MigrationScripts() []migration.Script {
-       return []migration.Script{
-               new(migrationscripts.InitSchemas),
-               new(migrationscripts.UpdateSchemas20220505),
-               new(migrationscripts.UpdateSchemas20220507),
-               new(migrationscripts.UpdateSchemas20220518),
-               new(migrationscripts.UpdateSchemas20220525),
-               new(migrationscripts.UpdateSchemas20220526),
-               new(migrationscripts.UpdateSchemas20220527),
-               new(migrationscripts.UpdateSchemas20220601),
-               new(migrationscripts.UpdateSchemas20220614),
-               new(migrationscripts.UpdateSchemas20220615),
-               new(migrationscripts.UpdateSchemas20220616),
-               new(migrationscripts.UpdateSchemas20220620),
-       }
-}
-
-func (plugin Jira) ApiResources() 
map[string]map[string]core.ApiResourceHandler {
-       return map[string]map[string]core.ApiResourceHandler{
-               "test": {
-                       "POST": api.TestConnection,
-               },
-               "echo": {
-                       "POST": func(input *core.ApiResourceInput) 
(*core.ApiResourceOutput, error) {
-                               return &core.ApiResourceOutput{Body: 
input.Body}, nil
-                       },
-               },
-               "connections": {
-                       "POST": api.PostConnections,
-                       "GET":  api.ListConnections,
-               },
-               "connections/:connectionId": {
-                       "PATCH":  api.PatchConnection,
-                       "DELETE": api.DeleteConnection,
-                       "GET":    api.GetConnection,
-               },
-               "connections/:connectionId/proxy/rest/*path": {
-                       "GET": api.Proxy,
-               },
-       }
-}
-
-// Export a variable named PluginEntry for Framework to search and load
-var PluginEntry Jira //nolint
+var PluginEntry impl.Jira //nolint
 
 // standalone mode for debugging
 func main() {
diff --git a/plugins/jira/tasks/board_collector.go 
b/plugins/jira/tasks/board_collector.go
index e2622bf2..2642a43f 100644
--- a/plugins/jira/tasks/board_collector.go
+++ b/plugins/jira/tasks/board_collector.go
@@ -30,6 +30,13 @@ const RAW_BOARD_TABLE = "jira_api_boards"
 
 var _ core.SubTaskEntryPoint = CollectBoard
 
+var CollectBoardMeta = core.SubTaskMeta{
+       Name:             "collectBoard",
+       EntryPoint:       CollectBoard,
+       EnabledByDefault: true,
+       Description:      "collect Jira board",
+}
+
 func CollectBoard(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        logger := taskCtx.GetLogger()
diff --git a/plugins/jira/tasks/board_convertor.go 
b/plugins/jira/tasks/board_convertor.go
index e1d05e9f..1fccfd09 100644
--- a/plugins/jira/tasks/board_convertor.go
+++ b/plugins/jira/tasks/board_convertor.go
@@ -29,6 +29,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertBoardMeta = core.SubTaskMeta{
+       Name:             "convertBoard",
+       EntryPoint:       ConvertBoard,
+       EnabledByDefault: true,
+       Description:      "convert Jira board",
+}
+
 func ConvertBoard(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        logger := taskCtx.GetLogger()
diff --git a/plugins/jira/tasks/board_extractor.go 
b/plugins/jira/tasks/board_extractor.go
index bb0344f2..cf00fec2 100644
--- a/plugins/jira/tasks/board_extractor.go
+++ b/plugins/jira/tasks/board_extractor.go
@@ -27,6 +27,12 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractBoard
 
+var ExtractBoardMeta = core.SubTaskMeta{Name: "extractBoard",
+       EntryPoint:       ExtractBoard,
+       EnabledByDefault: true,
+       Description:      "extract Jira board",
+}
+
 func ExtractBoard(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
 
diff --git a/plugins/jira/tasks/changelog_collector.go 
b/plugins/jira/tasks/changelog_collector.go
index d10d7908..09a9175a 100644
--- a/plugins/jira/tasks/changelog_collector.go
+++ b/plugins/jira/tasks/changelog_collector.go
@@ -35,6 +35,13 @@ var _ core.SubTaskEntryPoint = CollectChangelogs
 
 const RAW_CHANGELOG_TABLE = "jira_api_changelogs"
 
+var CollectChangelogsMeta = core.SubTaskMeta{
+       Name:             "collectChangelogs",
+       EntryPoint:       CollectChangelogs,
+       EnabledByDefault: true,
+       Description:      "collect Jira change logs",
+}
+
 func CollectChangelogs(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        if data.JiraServerInfo.DeploymentType == models.DeploymentServer {
diff --git a/plugins/jira/tasks/changelog_convertor.go 
b/plugins/jira/tasks/changelog_convertor.go
index 44c5171f..4b6fae3b 100644
--- a/plugins/jira/tasks/changelog_convertor.go
+++ b/plugins/jira/tasks/changelog_convertor.go
@@ -32,6 +32,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertChangelogsMeta = core.SubTaskMeta{
+       Name:             "convertChangelogs",
+       EntryPoint:       ConvertChangelogs,
+       EnabledByDefault: true,
+       Description:      "convert Jira change logs",
+}
+
 type ChangelogItemResult struct {
        models.JiraChangelogItem
        IssueId           uint64 `gorm:"index"`
diff --git a/plugins/jira/tasks/changelog_extractor.go 
b/plugins/jira/tasks/changelog_extractor.go
index 19de1041..f15c9be3 100644
--- a/plugins/jira/tasks/changelog_extractor.go
+++ b/plugins/jira/tasks/changelog_extractor.go
@@ -28,6 +28,13 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractChangelogs
 
+var ExtractChangelogsMeta = core.SubTaskMeta{
+       Name:             "extractChangelogs",
+       EntryPoint:       ExtractChangelogs,
+       EnabledByDefault: true,
+       Description:      "extract Jira change logs",
+}
+
 func ExtractChangelogs(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        if data.JiraServerInfo.DeploymentType == models.DeploymentServer {
diff --git a/plugins/jira/tasks/issue_collector.go 
b/plugins/jira/tasks/issue_collector.go
index e9d23c05..5b64d138 100644
--- a/plugins/jira/tasks/issue_collector.go
+++ b/plugins/jira/tasks/issue_collector.go
@@ -41,6 +41,13 @@ type JiraApiParams struct {
 
 var _ core.SubTaskEntryPoint = CollectIssues
 
+var CollectIssuesMeta = core.SubTaskMeta{
+       Name:             "collectIssues",
+       EntryPoint:       CollectIssues,
+       EnabledByDefault: true,
+       Description:      "collect Jira issues",
+}
+
 func CollectIssues(taskCtx core.SubTaskContext) error {
        db := taskCtx.GetDal()
        data := taskCtx.GetData().(*JiraTaskData)
diff --git a/plugins/jira/tasks/issue_commit_convertor.go 
b/plugins/jira/tasks/issue_commit_convertor.go
index de4e0030..c228859b 100644
--- a/plugins/jira/tasks/issue_commit_convertor.go
+++ b/plugins/jira/tasks/issue_commit_convertor.go
@@ -28,6 +28,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertIssueCommitsMeta = core.SubTaskMeta{
+       Name:             "convertIssueCommits",
+       EntryPoint:       ConvertIssueCommits,
+       EnabledByDefault: true,
+       Description:      "convert Jira issue commits",
+}
+
 func ConvertIssueCommits(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        db := taskCtx.GetDal()
diff --git a/plugins/jira/tasks/issue_convertor.go 
b/plugins/jira/tasks/issue_convertor.go
index b7df9133..22a55dcf 100644
--- a/plugins/jira/tasks/issue_convertor.go
+++ b/plugins/jira/tasks/issue_convertor.go
@@ -31,6 +31,13 @@ import (
        jiraModels "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertIssuesMeta = core.SubTaskMeta{
+       Name:             "convertIssues",
+       EntryPoint:       ConvertIssues,
+       EnabledByDefault: true,
+       Description:      "convert Jira issues",
+}
+
 func ConvertIssues(taskCtx core.SubTaskContext) error {
        db := taskCtx.GetDal()
        data := taskCtx.GetData().(*JiraTaskData)
diff --git a/plugins/jira/tasks/issue_extractor.go 
b/plugins/jira/tasks/issue_extractor.go
index b86c3946..84cf2abb 100644
--- a/plugins/jira/tasks/issue_extractor.go
+++ b/plugins/jira/tasks/issue_extractor.go
@@ -31,6 +31,13 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractIssues
 
+var ExtractIssuesMeta = core.SubTaskMeta{
+       Name:             "extractIssues",
+       EntryPoint:       ExtractIssues,
+       EnabledByDefault: true,
+       Description:      "extract Jira issues",
+}
+
 func ExtractIssues(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        connectionId := data.Options.ConnectionId
diff --git a/plugins/jira/tasks/issue_repo_commit_convertor.go 
b/plugins/jira/tasks/issue_repo_commit_convertor.go
index 2d47f42e..7de02746 100644
--- a/plugins/jira/tasks/issue_repo_commit_convertor.go
+++ b/plugins/jira/tasks/issue_repo_commit_convertor.go
@@ -29,6 +29,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertIssueRepoCommitsMeta = core.SubTaskMeta{
+       Name:             "convertIssueRepoCommits",
+       EntryPoint:       ConvertIssueRepoCommits,
+       EnabledByDefault: false,
+       Description:      "convert Jira issue repo commits",
+}
+
 // ConvertIssueRepoCommits is to extract issue_repo_commits from 
jira_issue_commits, nothing difference with
 // issue_commits but added a RepoUrl. This task is needed by EE group.
 func ConvertIssueRepoCommits(taskCtx core.SubTaskContext) error {
diff --git a/plugins/jira/tasks/project_collector.go 
b/plugins/jira/tasks/project_collector.go
index 287a9595..811acf90 100644
--- a/plugins/jira/tasks/project_collector.go
+++ b/plugins/jira/tasks/project_collector.go
@@ -30,6 +30,13 @@ const RAW_PROJECT_TABLE = "jira_api_projects"
 
 var _ core.SubTaskEntryPoint = CollectProjects
 
+var CollectProjectsMeta = core.SubTaskMeta{
+       Name:             "collectProjects",
+       EntryPoint:       CollectProjects,
+       EnabledByDefault: true,
+       Description:      "collect Jira projects",
+}
+
 func CollectProjects(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        logger := taskCtx.GetLogger()
diff --git a/plugins/jira/tasks/project_extractor.go 
b/plugins/jira/tasks/project_extractor.go
index b63fcb92..e37468ca 100644
--- a/plugins/jira/tasks/project_extractor.go
+++ b/plugins/jira/tasks/project_extractor.go
@@ -27,6 +27,13 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractProjects
 
+var ExtractProjectsMeta = core.SubTaskMeta{
+       Name:             "extractProjects",
+       EntryPoint:       ExtractProjects,
+       EnabledByDefault: true,
+       Description:      "extract Jira projects",
+}
+
 func ExtractProjects(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
diff --git a/plugins/jira/tasks/remotelink_collector.go 
b/plugins/jira/tasks/remotelink_collector.go
index 0a558386..77a78f23 100644
--- a/plugins/jira/tasks/remotelink_collector.go
+++ b/plugins/jira/tasks/remotelink_collector.go
@@ -32,6 +32,13 @@ const RAW_REMOTELINK_TABLE = "jira_api_remotelinks"
 
 var _ core.SubTaskEntryPoint = CollectRemotelinks
 
+var CollectRemotelinksMeta = core.SubTaskMeta{
+       Name:             "collectRemotelinks",
+       EntryPoint:       CollectRemotelinks,
+       EnabledByDefault: true,
+       Description:      "collect Jira remote links",
+}
+
 func CollectRemotelinks(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        db := taskCtx.GetDal()
diff --git a/plugins/jira/tasks/remotelink_extractor.go 
b/plugins/jira/tasks/remotelink_extractor.go
index 6c73efbc..fc743c78 100644
--- a/plugins/jira/tasks/remotelink_extractor.go
+++ b/plugins/jira/tasks/remotelink_extractor.go
@@ -28,6 +28,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
 )
 
+var ExtractRemotelinksMeta = core.SubTaskMeta{
+       Name:             "extractRemotelinks",
+       EntryPoint:       ExtractRemotelinks,
+       EnabledByDefault: true,
+       Description:      "extract Jira remote links",
+}
+
 func ExtractRemotelinks(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        connectionId := data.Options.ConnectionId
diff --git a/plugins/jira/tasks/sprint_collector.go 
b/plugins/jira/tasks/sprint_collector.go
index 07a0e5e0..6f10fb8b 100644
--- a/plugins/jira/tasks/sprint_collector.go
+++ b/plugins/jira/tasks/sprint_collector.go
@@ -31,6 +31,13 @@ const RAW_SPRINT_TABLE = "jira_api_sprints"
 
 var _ core.SubTaskEntryPoint = CollectSprints
 
+var CollectSprintsMeta = core.SubTaskMeta{
+       Name:             "collectSprints",
+       EntryPoint:       CollectSprints,
+       EnabledByDefault: true,
+       Description:      "collect Jira sprints",
+}
+
 func CollectSprints(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        logger := taskCtx.GetLogger()
diff --git a/plugins/jira/tasks/sprint_convertor.go 
b/plugins/jira/tasks/sprint_convertor.go
index 432f4302..af0d1c71 100644
--- a/plugins/jira/tasks/sprint_convertor.go
+++ b/plugins/jira/tasks/sprint_convertor.go
@@ -30,6 +30,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertSprintsMeta = core.SubTaskMeta{
+       Name:             "convertSprints",
+       EntryPoint:       ConvertSprints,
+       EnabledByDefault: true,
+       Description:      "convert Jira sprints",
+}
+
 func ConvertSprints(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        connectionId := data.Options.ConnectionId
diff --git a/plugins/jira/tasks/sprint_extractor.go 
b/plugins/jira/tasks/sprint_extractor.go
index d3f3c83a..bc9a588a 100644
--- a/plugins/jira/tasks/sprint_extractor.go
+++ b/plugins/jira/tasks/sprint_extractor.go
@@ -28,6 +28,13 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractSprints
 
+var ExtractSprintsMeta = core.SubTaskMeta{
+       Name:             "extractSprints",
+       EntryPoint:       ExtractSprints,
+       EnabledByDefault: true,
+       Description:      "extract Jira sprints",
+}
+
 func ExtractSprints(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
diff --git a/plugins/jira/tasks/sprint_issues_convertor.go 
b/plugins/jira/tasks/sprint_issues_convertor.go
index 49621ec5..c5401bfd 100644
--- a/plugins/jira/tasks/sprint_issues_convertor.go
+++ b/plugins/jira/tasks/sprint_issues_convertor.go
@@ -28,6 +28,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertSprintIssuesMeta = core.SubTaskMeta{
+       Name:             "convertSprintIssues",
+       EntryPoint:       ConvertSprintIssues,
+       EnabledByDefault: true,
+       Description:      "convert Jira sprint_issues",
+}
+
 func ConvertSprintIssues(taskCtx core.SubTaskContext) error {
        db := taskCtx.GetDal()
        data := taskCtx.GetData().(*JiraTaskData)
diff --git a/plugins/jira/tasks/status_collector.go 
b/plugins/jira/tasks/status_collector.go
index 01f4ef1a..da5bd952 100644
--- a/plugins/jira/tasks/status_collector.go
+++ b/plugins/jira/tasks/status_collector.go
@@ -27,6 +27,13 @@ import (
 
 const RAW_STATUS_TABLE = "jira_api_status"
 
+var CollectStatusMeta = core.SubTaskMeta{
+       Name:             "collectStatus",
+       EntryPoint:       CollectStatus,
+       EnabledByDefault: true,
+       Description:      "collect Jira status",
+}
+
 func CollectStatus(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
 
diff --git a/plugins/jira/tasks/status_extractor.go 
b/plugins/jira/tasks/status_extractor.go
index bf68ea92..73362814 100644
--- a/plugins/jira/tasks/status_extractor.go
+++ b/plugins/jira/tasks/status_extractor.go
@@ -26,6 +26,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
 )
 
+var ExtractStatusMeta = core.SubTaskMeta{
+       Name:             "extractStatus",
+       EntryPoint:       ExtractStatus,
+       EnabledByDefault: true,
+       Description:      "extract Jira status",
+}
+
 func ExtractStatus(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        connectionId := data.Options.ConnectionId
diff --git a/plugins/jira/tasks/user_collector.go 
b/plugins/jira/tasks/user_collector.go
index e43b0ee4..86bf3e60 100644
--- a/plugins/jira/tasks/user_collector.go
+++ b/plugins/jira/tasks/user_collector.go
@@ -31,6 +31,13 @@ import (
 
 const RAW_USERS_TABLE = "jira_api_users"
 
+var CollectUsersMeta = core.SubTaskMeta{
+       Name:             "collectUsers",
+       EntryPoint:       CollectUsers,
+       EnabledByDefault: true,
+       Description:      "collect Jira users",
+}
+
 func CollectUsers(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        db := taskCtx.GetDal()
diff --git a/plugins/jira/tasks/user_convertor.go 
b/plugins/jira/tasks/user_convertor.go
index 843cd578..a163d20e 100644
--- a/plugins/jira/tasks/user_convertor.go
+++ b/plugins/jira/tasks/user_convertor.go
@@ -28,6 +28,13 @@ import (
        "reflect"
 )
 
+var ConvertUsersMeta = core.SubTaskMeta{
+       Name:             "convertUsers",
+       EntryPoint:       ConvertUsers,
+       EnabledByDefault: true,
+       Description:      "convert Jira users",
+}
+
 func ConvertUsers(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        connectionId := data.Options.ConnectionId
diff --git a/plugins/jira/tasks/user_extractor.go 
b/plugins/jira/tasks/user_extractor.go
index 4b21be33..d117e827 100644
--- a/plugins/jira/tasks/user_extractor.go
+++ b/plugins/jira/tasks/user_extractor.go
@@ -27,6 +27,13 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractUsers
 
+var ExtractUsersMeta = core.SubTaskMeta{
+       Name:             "extractUsers",
+       EntryPoint:       ExtractUsers,
+       EnabledByDefault: true,
+       Description:      "extract Jira users",
+}
+
 func ExtractUsers(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
diff --git a/plugins/jira/tasks/worklog_collector.go 
b/plugins/jira/tasks/worklog_collector.go
index e3e1aec6..cdb11eea 100644
--- a/plugins/jira/tasks/worklog_collector.go
+++ b/plugins/jira/tasks/worklog_collector.go
@@ -30,6 +30,13 @@ import (
 
 const RAW_WORKLOGS_TABLE = "jira_api_worklogs"
 
+var CollectWorklogsMeta = core.SubTaskMeta{
+       Name:             "collectWorklogs",
+       EntryPoint:       CollectWorklogs,
+       EnabledByDefault: true,
+       Description:      "collect Jira work logs",
+}
+
 func CollectWorklogs(taskCtx core.SubTaskContext) error {
        db := taskCtx.GetDal()
        data := taskCtx.GetData().(*JiraTaskData)
diff --git a/plugins/jira/tasks/worklog_convertor.go 
b/plugins/jira/tasks/worklog_convertor.go
index 5fd6f21f..2f9385d4 100644
--- a/plugins/jira/tasks/worklog_convertor.go
+++ b/plugins/jira/tasks/worklog_convertor.go
@@ -29,6 +29,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/jira/models"
 )
 
+var ConvertWorklogsMeta = core.SubTaskMeta{
+       Name:             "convertWorklogs",
+       EntryPoint:       ConvertWorklogs,
+       EnabledByDefault: true,
+       Description:      "convert Jira work logs",
+}
+
 func ConvertWorklogs(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        db := taskCtx.GetDal()
diff --git a/plugins/jira/tasks/worklog_extractor.go 
b/plugins/jira/tasks/worklog_extractor.go
index 0f567891..698c9c63 100644
--- a/plugins/jira/tasks/worklog_extractor.go
+++ b/plugins/jira/tasks/worklog_extractor.go
@@ -26,6 +26,13 @@ import (
 
 var _ core.SubTaskEntryPoint = ExtractWorklogs
 
+var ExtractWorklogsMeta = core.SubTaskMeta{
+       Name:             "extractWorklogs",
+       EntryPoint:       ExtractWorklogs,
+       EnabledByDefault: true,
+       Description:      "extract Jira work logs",
+}
+
 func ExtractWorklogs(taskCtx core.SubTaskContext) error {
        data := taskCtx.GetData().(*JiraTaskData)
        extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{

Reply via email to