This is an automated email from the ASF dual-hosted git repository.
zhangliang2022 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 11036ff83 feat: add bug_commit collector,extractor and
bug_repo_commit_collector (#5343)
11036ff83 is described below
commit 11036ff8379cfa6072005903135581b5a558ef95
Author: abeizn <[email protected]>
AuthorDate: Mon Jun 5 14:45:21 2023 +0800
feat: add bug_commit collector,extractor and bug_repo_commit_collector
(#5343)
* feat: add bug_commit collector,extractor and bug_repo_commit_collector
* feat: add bug_commit collector,extractor and bug_repo_commit_collector
* feat: zentao add bug repo commits extractor
* fix: convertor task
* feat: zentao bug repo commits convertor and add e2e
* fix: zentao e2e
* fix: zentao e2e
* fix: bug_repo_commits_collector
* feat: add zentao story repo commits
* fix: regexp just gets compiled once and check first whether the array is
out of bounds
* feat: zentao task repo commits and e2e
* fix: impl and some lint
---
backend/plugins/zentao/e2e/bug_commits_test.go | 76 +++++++++++
.../e2e/raw_tables/_raw_zentao_api_bug_commits.csv | 25 ++++
.../_raw_zentao_api_bug_repo_commits.csv | 2 +
.../raw_tables/_raw_zentao_api_story_commits.csv | 21 +++
.../_raw_zentao_api_story_repo_commits.csv | 2 +
.../raw_tables/_raw_zentao_api_task_commits.csv | 7 +
.../_raw_zentao_api_task_repo_commits.csv | 2 +
.../snapshot_tables/_tool_zentao_bug_commits.csv | 3 +
.../_tool_zentao_bug_repo_commits.csv | 2 +
.../snapshot_tables/_tool_zentao_story_commits.csv | 3 +
.../_tool_zentao_story_repo_commits.csv | 2 +
.../snapshot_tables/_tool_zentao_task_commits.csv | 3 +
.../_tool_zentao_task_repo_commits.csv | 2 +
.../e2e/snapshot_tables/issue_bug_repo_commits.csv | 2 +
.../snapshot_tables/issue_story_repo_commits.csv | 2 +
.../snapshot_tables/issue_task_repo_commits.csv | 2 +
backend/plugins/zentao/e2e/story_commits_test.go | 76 +++++++++++
backend/plugins/zentao/e2e/task_commits_test.go | 76 +++++++++++
backend/plugins/zentao/impl/impl.go | 17 +++
.../plugins/zentao/models/archived/bug_commits.go | 41 +++++-
.../zentao/models/archived/story_commits.go | 62 +++++++++
.../plugins/zentao/models/archived/task_commits.go | 62 +++++++++
backend/plugins/zentao/models/bug_commits.go | 131 ++++++++++++++++--
...mmits.go => 20230605_add_issue_repo_commits.go} | 19 ++-
.../zentao/models/migrationscripts/register.go | 2 +-
backend/plugins/zentao/models/story_commits.go | 151 +++++++++++++++++++++
backend/plugins/zentao/models/task_commits.go | 151 +++++++++++++++++++++
.../plugins/zentao/tasks/bug_commits_collector.go | 28 ++--
.../plugins/zentao/tasks/bug_commits_extractor.go | 113 +++++++++++++++
..._collector.go => bug_repo_commits_collector.go} | 91 +++++--------
.../zentao/tasks/bug_repo_commits_convertor.go | 93 +++++++++++++
.../zentao/tasks/bug_repo_commits_extractor.go | 91 +++++++++++++
backend/plugins/zentao/tasks/shared.go | 35 +++++
...its_collector.go => story_commits_collector.go} | 60 ++++----
.../zentao/tasks/story_commits_extractor.go | 115 ++++++++++++++++
...ollector.go => story_repo_commits_collector.go} | 87 +++++-------
.../zentao/tasks/story_repo_commits_convertor.go | 93 +++++++++++++
.../zentao/tasks/story_repo_commits_extractor.go | 91 +++++++++++++
...mits_collector.go => task_commits_collector.go} | 69 +++++-----
.../plugins/zentao/tasks/task_commits_extractor.go | 115 ++++++++++++++++
...collector.go => task_repo_commits_collector.go} | 91 +++++--------
.../zentao/tasks/task_repo_commits_convertor.go | 93 +++++++++++++
.../zentao/tasks/task_repo_commits_extractor.go | 91 +++++++++++++
43 files changed, 2028 insertions(+), 272 deletions(-)
diff --git a/backend/plugins/zentao/e2e/bug_commits_test.go
b/backend/plugins/zentao/e2e/bug_commits_test.go
new file mode 100644
index 000000000..5bd50fdc1
--- /dev/null
+++ b/backend/plugins/zentao/e2e/bug_commits_test.go
@@ -0,0 +1,76 @@
+/*
+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/core/models/common"
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/zentao/impl"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+ "github.com/apache/incubator-devlake/plugins/zentao/tasks"
+)
+
+func TestZentaoBugCommitsDataFlow(t *testing.T) {
+
+ var zentao impl.Zentao
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao)
+
+ taskData := &tasks.ZentaoTaskData{
+ Options: &tasks.ZentaoOptions{
+ ConnectionId: 1,
+ ProjectId: 0,
+ ProductId: 22,
+ },
+ }
+
+ // import _raw_zentao_api_bug_commits raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_bug_commits.csv",
+ "_raw_zentao_api_bug_commits")
+
+ // verify bug commit extraction
+ dataflowTester.FlushTabler(&models.ZentaoBugCommit{})
+ dataflowTester.Subtask(tasks.ExtractBugCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&models.ZentaoBugCommit{},
e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_zentao_bug_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+ // import _raw_zentao_api_bug_repo_commits raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_bug_repo_commits.csv",
+ "_raw_zentao_api_bug_repo_commits")
+
+ // verify bug repo commit extraction
+ dataflowTester.FlushTabler(&models.ZentaoBugRepoCommit{})
+ dataflowTester.Subtask(tasks.ExtractBugRepoCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&models.ZentaoBugRepoCommit{},
e2ehelper.TableOptions{
+ CSVRelPath:
"./snapshot_tables/_tool_zentao_bug_repo_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+ // verify bug repo commit conversion
+ dataflowTester.FlushTabler(&crossdomain.IssueRepoCommit{})
+ dataflowTester.Subtask(tasks.ConvertBugRepoCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&crossdomain.IssueRepoCommit{},
e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_bug_repo_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+}
diff --git
a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bug_commits.csv
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bug_commits.csv
new file mode 100644
index 000000000..01ec64662
--- /dev/null
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bug_commits.csv
@@ -0,0 +1,25 @@
+id,params,data,url,input,created_at
+1003,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":15826,""objectType"":""bug"",""objectID"":6060,""product"":"""",""project"":0,""execution"":0,""actor"":""gerile
tu"",""action"":""commented"",""date"":""2023-02-20
03:53:48"",""comment"":""\u6240\u6709\u56fe\u8868\u90fd\u662f\u624b\u578b\uff0c\u4e0d\u4f1a\u6839\u636e\u662f\u5426\u914d\u7f6e\u4e86interactions\u800c\u6539\u53d8\u6837\u5f0f\uff0c\u56fe\u8868\u548cinteractions\u662f\u4e24\u6761\u5e76\u884c\u7684\u529f\u80
[...]
+1004,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13832,""objectType"":""bug"",""objectID"":6067,""product"":"""",""project"":0,""execution"":0,""actor"":""\u674e\u8f89"",""action"":""Opened"",""date"":""2023-05-10
09:11:08"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-10
09:11:08, \u7531 \u003Cstrong\u003E\u674e\u8f89\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1 [...]
+1005,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13833,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""57333edf-bbf8-424f-9d17-4956e0391a37"",""action"":""Opened"",""date"":""2023-01-03
09:16:03"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-01-03
09:16:03, \u7531
\u003Cstrong\u003E57333edf-bbf8-424f-9d17-4956e0391a37\u003C\/strong\u003E
\u521b\u5efa [...]
+1006,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14117,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-01-03
09:38:13"",""comment"":""cag_nodes\u4e2d\u7684frequent_hexsha\u5728commits\u8868\u4e2d\u627e\u4e0d\u5230\uff0c\u9700\u8981AE\u540c\u5b66\u5e2e\u5fd9\u770b\u770b\n\n!image-20230103-093806.png|width=1523,height=217!"",""extra"":null
[...]
+1007,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14122,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-01-03
09:44:19"",""comment"":""{code:sql}select cag_nodes.frequent_hexsha,
commits.hexsha from cag_nodes left join commits on cag_nodes.analysis_id =
commits.analysis_id and cag_nodes.frequent_hexsha = commits.hexsha where
cag_nodes.ana [...]
+1008,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14131,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0d8c496d-fd2b-486d-a139-1a90d17cc871"",""action"":""commented"",""date"":""2023-01-03
16:25:31"",""comment"":""[~accountid:5fa8b6d142ab3b006eaa6f42]
CAG_NODES\u8868\u683c\u4e2d\u7684\u63d0\u4ea4\u4fe1\u606f\u6765\u81ea\u4e8eblame\u5904\u7406\u5206\u6790\uff0ccommits\u8868\u683c\u4e2d\u7684\u63d0\u4ea4\u662f\u904d\u
[...]
+1009,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14132,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-01-04
00:46:48"",""comment"":""\u770b\u4e86\u4e00\u4e0b\u76ee\u524dvdev.co\u4ee3\u7801\u5e93\u4e2d\u786e\u5b9e\u4e0d\u5b58\u5728\u8fd9\u4e24\u4e2acommit
hash\u3002\n\n{quote}\u5728blame\u4e2d\u8fd8\u6709\u5bf9\u5e94\u4fe1\u606f\uff0c\u8
[...]
+1010,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14188,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-01-04
02:53:00"",""comment"":""AE\u4fa7\u6392\u67e5\u4fee\u590d\u4e2d"",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-01-04
02:53:00, \u7531 \u003Cstrong\u003E0380e3fd-c867-4a94-aff6-e0f3 [...]
+1011,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14258,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0d8c496d-fd2b-486d-a139-1a90d17cc871"",""action"":""commented"",""date"":""2023-01-05
01:02:40"",""comment"":""AE\u4fa7\u7ed1\u5b9a\u7684MR\uff1a[https:\/\/gitlab.com\/merico-dev\/ae\/meta-analytics\/-\/merge_requests\/2347|https:\/\/gitlab.com\/merico-dev\/ae\/meta-analytics\/-\/merge_requests\/2347]"",""extra"":n
[...]
+1012,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14936,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-01-13
02:41:34"",""comment"":""\u5c1a\u672a\u5408\u5e76\uff0c\u672c\u8fed\u4ee3\u53d1\u7248\u6709\u98ce\u9669\u3002"",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-01-13
02:41:34, \u7531 [...]
+1013,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14403,""objectType"":""bug"",""objectID"":6068,""product"":"""",""project"":0,""execution"":0,""actor"":""0d8c496d-fd2b-486d-a139-1a90d17cc871"",""action"":""commented"",""date"":""2023-02-03
02:40:36"",""comment"":""2.30.4-stable\u8fdb\u884c\u4e86\u5408\u5e76\u53d1\u5e03\u3002"",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-02-03
02:40:36, \u7531 \u003Cstrong\u003E0d8c4 [...]
+1014,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13842,""objectType"":""bug"",""objectID"":6071,""product"":"""",""project"":0,""execution"":0,""actor"":""\u674e\u8f89"",""action"":""Opened"",""date"":""2023-05-10
09:24:42"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-10
09:24:42, \u7531 \u003Cstrong\u003E\u674e\u8f89\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1 [...]
+1015,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13808,""objectType"":""bug"",""objectID"":6054,""product"":"""",""project"":0,""execution"":0,""actor"":""\u674e\u8f89"",""action"":""Opened"",""date"":""2023-03-07
03:38:20"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-03-07
03:38:20, \u7531 \u003Cstrong\u003E\u674e\u8f89\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1 [...]
+1016,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":16534,""objectType"":""bug"",""objectID"":6054,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-03-07
03:41:33"",""comment"":""\u5177\u4f53\u70b9\uff0c\u662f\u54ea\u4e2a\u5b57\u6bb5\u5bf9\u4e0d\u4e0a\u5427\u3002\u3002
\u8fd9\u6837\u770b\u4e0d\u660e\u767d"",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history""
[...]
+1017,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":16616,""objectType"":""bug"",""objectID"":6054,""product"":"""",""project"":0,""execution"":0,""actor"":""\u674e\u8f89"",""action"":""commented"",""date"":""2023-03-08
01:14:50"",""comment"":""[~accountid:5fa8b6d142ab3b006eaa6f42]
\u8fed\u4ee3\u5f00\u53d1\u770b\u677f\uff0c\u51b2\u523a13\uff0c\u4e4b\u524d\u81ea\u52a8\u5316\u9884\u671f\u503c\u662f174\uff0c\u73b0\u5728\u7ed3\u679c\u662f173\uff0c\u7ed3\u679c\u5b58\u5728\u5
[...]
+1018,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":14449,""objectType"":""bug"",""objectID"":6054,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-03-08
07:01:01"",""comment"":""\u76ee\u524d\u53ea\u80fd\u770b\u5230\u8fd9\u51e0\u4e2a\u6570\u636e\u53d8\u5316\u4e86\u3002\u4f46\u6ca1\u6709\u53c2\u7167\uff0c\u67e5\u8be2\u96be\u4ee5\u5165\u624b\u3002
\u5efa\u8bae\u5728\u6d4b\u8bd5\u7528 [...]
+1019,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13846,""objectType"":""bug"",""objectID"":6073,""product"":"""",""project"":0,""execution"":0,""actor"":""\u674e\u8f89"",""action"":""Opened"",""date"":""2023-05-10
09:34:26"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-10
09:34:26, \u7531 \u003Cstrong\u003E\u674e\u8f89\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1 [...]
+1020,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":16653,""objectType"":""bug"",""objectID"":6073,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""commented"",""date"":""2023-05-10
10:21:00"",""comment"":""\u8fd9\u5e94\u8be5\u662f\u4e00\u4e2amaster\u4e0a\u5c31\u6709\u7684\u95ee\u9898\u3002\u5728master\u4e0a\u5f00\u5206\u652f\u4fee\u590d"",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""hist
[...]
+1021,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13857,""objectType"":""bug"",""objectID"":6082,""product"":"""",""project"":0,""execution"":0,""actor"":""379e86b9-4985-438f-9eed-cd747bb78c54"",""action"":""Opened"",""date"":""2023-05-10
10:02:14"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-10
10:02:14, \u7531
\u003Cstrong\u003E379e86b9-4985-438f-9eed-cd747bb78c54\u003C\/strong\u003E
\u521b\u5efa [...]
+1022,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13861,""objectType"":""bug"",""objectID"":6084,""product"":"""",""project"":0,""execution"":0,""actor"":""0380e3fd-c867-4a94-aff6-e0f3fedea2f5"",""action"":""Opened"",""date"":""2023-03-02
03:40:18"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-03-02
03:40:18, \u7531
\u003Cstrong\u003E0380e3fd-c867-4a94-aff6-e0f3fedea2f5\u003C\/strong\u003E
\u521b\u5efa [...]
+1023,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":13862,""objectType"":""bug"",""objectID"":6085,""product"":"""",""project"":0,""execution"":0,""actor"":""lin.hao"",""action"":""Opened"",""date"":""2023-05-09
10:47:13"",""comment"":"""",""extra"":null,""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-09
10:47:13, \u7531 \u003Cstrong\u003Elin.hao\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1/bugs/6085 [...]
+1024,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":37654,""objectType"":""bug"",""objectID"":6094,""product"":"",22,"",""project"":48,""execution"":49,""actor"":""admin"",""action"":""opened"",""date"":""2023-05-30
21:02:00"",""comment"":"""",""extra"":"""",""read"":""1"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-30
21:02:00, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1/bugs/60 [...]
+1025,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":37655,""objectType"":""bug"",""objectID"":6094,""product"":"",22,"",""project"":0,""execution"":49,""actor"":""Administrator"",""action"":""gitcommited"",""date"":""2023-05-30
21:02:28"",""comment"":""\u7248\u672c: #9ed3c33883\u003Cbr
\/\u003EBug#6094\uff0c\u6d4b\u8bd5\u7985\u9053bug"",""extra"":""9ed3c33883"",""read"":""1"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":87,""action"":37655,""field"":""git"",""
[...]
+1026,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""id"":37656,""objectType"":""bug"",""objectID"":6094,""product"":"",22,"",""project"":48,""execution"":49,""actor"":""Administrator"",""action"":""linked2revision"",""date"":""2023-05-30
21:03:07"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/repo-revision-1-0-9ed3c33883a203e0cc90c44be40d105b23d98156.json'
data-app='product'\u003E9ed3c33883\u003C\/a\u003E\n"",""read"":""1"",""vision"":""rnd"",""eff
[...]
diff --git
a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bug_repo_commits.csv
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bug_repo_commits.csv
new file mode 100644
index 000000000..5c44aefc0
--- /dev/null
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bug_repo_commits.csv
@@ -0,0 +1,2 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""ProductId"":22,""ProjectId"":0}","{""title"":""\u4ee3\u7801-\u67e5\u770b\u4fee\u8ba2"",""log"":{""revision"":""9ed3c33883a203e0cc90c44be40d105b23d98156"",""committer"":""Administrator"",""time"":""2023-05-30
21:02:28"",""comment"":""Bug #<a href='\/bug-view-6094.json'
>6094<\/a>\n\uff0c\u6d4b\u8bd5\u7985\u9053bug"",""change"":{""\/test.yaml"":{""action"":""M"",""kind"":""file"",""oldPath"":""\/test.yaml""}},""commit"":""3""},""repo"":{""id"":""1"",""product"":""
[...]
diff --git
a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_story_commits.csv
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_story_commits.csv
new file mode 100644
index 000000000..6e95c27d1
--- /dev/null
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_story_commits.csv
@@ -0,0 +1,21 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37626,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""opened"",""date"":""2023-05-18
18:58:51"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-18
18:58:51, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1/stories/45 [...]
+2,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37627,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""submitreview"",""date"":""2023-05-18
18:58:51"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-18
18:58:51, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u63d0\u4ea4\u8bc4\u5ba1\u3002\n""}",http://54.158.1.10:30001/api [...]
+3,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37628,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""reviewed"",""date"":""2023-05-18
18:59:04"",""comment"":"""",""extra"":""Pass"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":72,""action"":37628,""field"":""reviewedDate"",""old"":""0000-00-00
00:00:00"",""new"":""2023-05-18 18:58:59"",""diff"":"""",""fieldName"":""\
[...]
+4,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37629,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""\u7cfb\u7edf"",""action"":""reviewpassed"",""date"":""2023-05-18
18:59:04"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-18
18:59:04, \u7531 \u003Cstrong\u003E\u7cfb\u7edf\u003C\/strong\u003E
\u5224\u5b9a\uff0c\u7ed3\u679c\u4e3a \u003Cstrong\u [...]
+5,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37630,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""assigned"",""date"":""2023-05-18
18:59:12"",""comment"":"""",""extra"":""admin"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":75,""action"":37630,""field"":""assignedTo"",""old"":"""",""new"":""admin"",""diff"":"""",""fieldName"":""\u6307\u6d3e\u7ed9""}],""desc"":""2
[...]
+6,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37631,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":0,""actor"":""admin"",""action"":""linked2project"",""date"":""2023-05-18
19:07:48"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/project-index-65.json'
\u003E\u66f2\u7387\u5f15\u64ce\u9879\u76ee-\u770b\u677f\u003C\/a\u003E\n"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"
[...]
+7,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37632,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":68,""actor"":""admin"",""action"":""linked2kanban"",""date"":""2023-05-18
19:07:48"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/execution-kanban-68.json'
data-app='execution'\u003E\u524d\u7aef\u770b\u677f\u003C\/a\u003E\n"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":"
[...]
+8,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37633,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":68,""actor"":""admin"",""action"":""edited"",""date"":""2023-05-18
19:08:21"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":76,""action"":37633,""field"":""stage"",""old"":""projected"",""new"":""developing"",""diff"":"""",""fieldName"":""\u6240\u5904\u9636\u6bb5""}],""
[...]
+9,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37651,""objectType"":""story"",""objectID"":4564,""product"":""31"",""project"":0,""execution"":0,""actor"":""Administrator"",""action"":""gitcommited"",""date"":""2023-05-30
20:58:31"",""comment"":""\u7248\u672c: #659ca32343\u003Cbr
\/\u003Estory#4564,\u7985\u9053\u6d4b\u8bd5"",""extra"":""659ca32343"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":86,""action"":37651,""field"":""git"",""old"":""""
[...]
+10,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37652,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":68,""actor"":""Administrator"",""action"":""linked2revision"",""date"":""2023-05-30
20:58:39"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/repo-revision-1-0-659ca323434f22ed01145de9d52eeb0d8288d8bb.json'
\u003E659ca32343\u003C\/a\u003E\n"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""histor
[...]
+11,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37626,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""opened"",""date"":""2023-05-18
18:58:51"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-18
18:58:51, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1/stories/4 [...]
+12,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37627,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""submitreview"",""date"":""2023-05-18
18:58:51"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-18
18:58:51, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u63d0\u4ea4\u8bc4\u5ba1\u3002\n""}",http://54.158.1.10:30001/ap [...]
+13,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37628,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""reviewed"",""date"":""2023-05-18
18:59:04"",""comment"":"""",""extra"":""Pass"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":72,""action"":37628,""field"":""reviewedDate"",""old"":""0000-00-00
00:00:00"",""new"":""2023-05-18 18:58:59"",""diff"":"""",""fieldName"":"" [...]
+14,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37629,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""\u7cfb\u7edf"",""action"":""reviewpassed"",""date"":""2023-05-18
18:59:04"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2023-05-18
18:59:04, \u7531 \u003Cstrong\u003E\u7cfb\u7edf\u003C\/strong\u003E
\u5224\u5b9a\uff0c\u7ed3\u679c\u4e3a \u003Cstrong\ [...]
+15,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37630,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":0,""execution"":0,""actor"":""admin"",""action"":""assigned"",""date"":""2023-05-18
18:59:12"",""comment"":"""",""extra"":""admin"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":75,""action"":37630,""field"":""assignedTo"",""old"":"""",""new"":""admin"",""diff"":"""",""fieldName"":""\u6307\u6d3e\u7ed9""}],""desc"":""
[...]
+16,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37631,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":0,""actor"":""admin"",""action"":""linked2project"",""date"":""2023-05-18
19:07:48"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/project-index-65.json'
\u003E\u66f2\u7387\u5f15\u64ce\u9879\u76ee-\u770b\u677f\u003C\/a\u003E\n"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc
[...]
+17,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37632,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":68,""actor"":""admin"",""action"":""linked2kanban"",""date"":""2023-05-18
19:07:48"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/execution-kanban-68.json'
data-app='execution'\u003E\u524d\u7aef\u770b\u677f\u003C\/a\u003E\n"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":
[...]
+18,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37633,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":68,""actor"":""admin"",""action"":""edited"",""date"":""2023-05-18
19:08:21"",""comment"":"""",""extra"":"""",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":76,""action"":37633,""field"":""stage"",""old"":""projected"",""new"":""developing"",""diff"":"""",""fieldName"":""\u6240\u5904\u9636\u6bb5""}],"
[...]
+19,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37651,""objectType"":""story"",""objectID"":4564,""product"":""31"",""project"":0,""execution"":0,""actor"":""Administrator"",""action"":""gitcommited"",""date"":""2023-05-30
20:58:31"",""comment"":""\u7248\u672c: #659ca32343\u003Cbr
\/\u003Estory#4564,\u7985\u9053\u6d4b\u8bd5"",""extra"":""659ca32343"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":86,""action"":37651,""field"":""git"",""old"":"""
[...]
+20,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""id"":37652,""objectType"":""story"",""objectID"":4564,""product"":"",31,"",""project"":65,""execution"":68,""actor"":""Administrator"",""action"":""linked2revision"",""date"":""2023-05-30
20:58:39"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/repo-revision-1-0-659ca323434f22ed01145de9d52eeb0d8288d8bb.json'
\u003E659ca32343\u003C\/a\u003E\n"",""read"":""0"",""vision"":""rnd"",""efforted"":0,""histor
[...]
diff --git
a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_story_repo_commits.csv
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_story_repo_commits.csv
new file mode 100644
index 000000000..defeb0f8e
--- /dev/null
+++
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_story_repo_commits.csv
@@ -0,0 +1,2 @@
+id,params,data,url,input,created_at
+3,"{""ConnectionId"":1,""ProductId"":31,""ProjectId"":0}","{""title"":""\u4ee3\u7801-\u67e5\u770b\u4fee\u8ba2"",""log"":{""revision"":""659ca323434f22ed01145de9d52eeb0d8288d8bb"",""committer"":""Administrator"",""time"":""2023-05-30
20:58:31"",""comment"":""story #<a href='\/story-view-4564.json'
>4564<\/a>\n,\u7985\u9053\u6d4b\u8bd5"",""change"":{""\/test.yaml"":{""action"":""M"",""kind"":""file"",""oldPath"":""\/test.yaml""}},""commit"":""2""},""repo"":{""id"":""1"",""product"":""22""
[...]
diff --git
a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_task_commits.csv
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_task_commits.csv
new file mode 100644
index 000000000..3199bd471
--- /dev/null
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_task_commits.csv
@@ -0,0 +1,7 @@
+id,params,data,url,input,created_at
+8554,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""id"":381,""objectType"":""task"",""objectID"":135,""product"":"""",""project"":0,""execution"":0,""actor"":""admin"",""action"":""Opened"",""date"":""2021-01-27
04:17:22"",""comment"":"""",""extra"":null,""read"":""1"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2021-01-27
04:17:22, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1/tasks/135,"{""i
[...]
+8555,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""id"":37658,""objectType"":""task"",""objectID"":135,""product"":"",22,"",""project"":0,""execution"":49,""actor"":""Administrator"",""action"":""gitcommited"",""date"":""2023-05-30
21:05:48"",""comment"":""\u7248\u672c: #ad92faa87e\u003Cbr \/\u003Etask#135,
\u7985\u9053task\u6d4b\u8bd5"",""extra"":""ad92faa87e"",""read"":""1"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":88,""action"":37658,""field"":""git"",""old
[...]
+8556,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""id"":37659,""objectType"":""task"",""objectID"":135,""product"":"",22,"",""project"":48,""execution"":49,""actor"":""Administrator"",""action"":""linked2revision"",""date"":""2023-05-30
21:05:56"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/repo-revision-1-0-ad92faa87e6eaf121d382f0f775cd6b98c5e65fe.json'
\u003Ead92faa87e\u003C\/a\u003E\n"",""read"":""1"",""vision"":""rnd"",""efforted"":0,""histor
[...]
+8558,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""id"":381,""objectType"":""task"",""objectID"":135,""product"":"""",""project"":0,""execution"":0,""actor"":""admin"",""action"":""Opened"",""date"":""2021-01-27
04:17:22"",""comment"":"""",""extra"":null,""read"":""1"",""vision"":""rnd"",""efforted"":0,""history"":[],""desc"":""2021-01-27
04:17:22, \u7531 \u003Cstrong\u003Eadmin\u003C\/strong\u003E
\u521b\u5efa\u3002\n""}",http://54.158.1.10:30001/api.php/v1/tasks/135,"{""i
[...]
+8559,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""id"":37658,""objectType"":""task"",""objectID"":135,""product"":"",22,"",""project"":0,""execution"":49,""actor"":""Administrator"",""action"":""gitcommited"",""date"":""2023-05-30
21:05:48"",""comment"":""\u7248\u672c: #ad92faa87e\u003Cbr \/\u003Etask#135,
\u7985\u9053task\u6d4b\u8bd5"",""extra"":""ad92faa87e"",""read"":""1"",""vision"":""rnd"",""efforted"":0,""history"":[{""id"":88,""action"":37658,""field"":""git"",""old
[...]
+8560,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""id"":37659,""objectType"":""task"",""objectID"":135,""product"":"",22,"",""project"":48,""execution"":49,""actor"":""Administrator"",""action"":""linked2revision"",""date"":""2023-05-30
21:05:56"",""comment"":"""",""extra"":""\u003Ca
href='http:\/\/54.158.1.10:30001\/repo-revision-1-0-ad92faa87e6eaf121d382f0f775cd6b98c5e65fe.json'
\u003Ead92faa87e\u003C\/a\u003E\n"",""read"":""1"",""vision"":""rnd"",""efforted"":0,""histor
[...]
diff --git
a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_task_repo_commits.csv
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_task_repo_commits.csv
new file mode 100644
index 000000000..6843d683e
--- /dev/null
+++
b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_task_repo_commits.csv
@@ -0,0 +1,2 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""ProductId"":0,""ProjectId"":48}","{""title"":""\u4ee3\u7801-\u67e5\u770b\u4fee\u8ba2"",""log"":{""revision"":""ad92faa87e6eaf121d382f0f775cd6b98c5e65fe"",""committer"":""Administrator"",""time"":""2023-05-30
21:05:48"",""comment"":""task #<a href='\/task-view-135.json' >135<\/a>\n,
\u7985\u9053task\u6d4b\u8bd5"",""change"":{""\/test.yaml"":{""action"":""M"",""kind"":""file"",""oldPath"":""\/test.yaml""}},""commit"":""4""},""repo"":{""id"":""1"",""product"":""22"
[...]
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bug_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bug_commits.csv
new file mode 100644
index 000000000..57a2f5e55
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bug_commits.csv
@@ -0,0 +1,3 @@
+connection_id,id,object_type,object_id,product,project,execution,actor,action,date,comment,extra,host,repo_revision,action_read,vision,efforted,action_desc
+1,37656,bug,6094,22,0,49,Administrator,linked2revision,2023-05-30
21:03:07,,http://54.158.1.10:30001/repo-revision-1-0-9ed3c33883a203e0cc90c44be40d105b23d98156.json,54.158.1.10:30001,/repo-revision-1-0-9ed3c33883a203e0cc90c44be40d105b23d98156.json,1,rnd,0,"2023-05-30
21:03:07, 由 <strong>Administrator</strong> 关联到代码提交 <strong><a
href='http://54.158.1.10:30001/repo-revision-1-0-9ed3c33883a203e0cc90c44be40d105b23d98156.json'
data-app='product'>9ed3c33883</a>
+</strong>."
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bug_repo_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bug_repo_commits.csv
new file mode 100644
index 000000000..9b0caab4a
--- /dev/null
+++
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bug_repo_commits.csv
@@ -0,0 +1,2 @@
+connection_id,issue_id,repo_url,commit_sha,product,project
+1,6094,http://devlake.gitlab.com/root/zentao-test,9ed3c33883a203e0cc90c44be40d105b23d98156,22,0
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_story_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_story_commits.csv
new file mode 100644
index 000000000..caa2b1120
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_story_commits.csv
@@ -0,0 +1,3 @@
+connection_id,id,object_type,object_id,product,project,execution,actor,action,date,comment,extra,host,repo_revision,action_read,vision,efforted,action_desc
+1,37652,story,4564,31,0,68,Administrator,linked2revision,2023-05-30
20:58:39,,http://54.158.1.10:30001/repo-revision-1-0-659ca323434f22ed01145de9d52eeb0d8288d8bb.json,54.158.1.10:30001,/repo-revision-1-0-659ca323434f22ed01145de9d52eeb0d8288d8bb.json,0,rnd,0,"2023-05-30
20:58:39, 由 <strong>Administrator</strong> 关联到代码提交 <strong><a
href='http://54.158.1.10:30001/repo-revision-1-0-659ca323434f22ed01145de9d52eeb0d8288d8bb.json'
>659ca32343</a>
+</strong>"
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_story_repo_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_story_repo_commits.csv
new file mode 100644
index 000000000..6954fde26
--- /dev/null
+++
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_story_repo_commits.csv
@@ -0,0 +1,2 @@
+connection_id,issue_id,repo_url,commit_sha,product,project
+1,4564,http://devlake.gitlab.com/root/zentao-test,659ca323434f22ed01145de9d52eeb0d8288d8bb,31,0
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_task_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_task_commits.csv
new file mode 100644
index 000000000..79b8978f7
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_task_commits.csv
@@ -0,0 +1,3 @@
+connection_id,id,object_type,object_id,product,project,execution,actor,action,date,comment,extra,host,repo_revision,action_read,vision,efforted,action_desc
+1,37659,task,135,0,48,49,Administrator,linked2revision,2023-05-30
21:05:56,,http://54.158.1.10:30001/repo-revision-1-0-ad92faa87e6eaf121d382f0f775cd6b98c5e65fe.json,54.158.1.10:30001,/repo-revision-1-0-ad92faa87e6eaf121d382f0f775cd6b98c5e65fe.json,1,rnd,0,"2023-05-30
21:05:56, 由 <strong>Administrator</strong> 关联到代码提交 <strong><a
href='http://54.158.1.10:30001/repo-revision-1-0-ad92faa87e6eaf121d382f0f775cd6b98c5e65fe.json'
>ad92faa87e</a>
+</strong>。"
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_task_repo_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_task_repo_commits.csv
new file mode 100644
index 000000000..b098b25ea
--- /dev/null
+++
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_task_repo_commits.csv
@@ -0,0 +1,2 @@
+connection_id,issue_id,repo_url,commit_sha,product,project
+1,135,http://devlake.gitlab.com/root/zentao-test,ad92faa87e6eaf121d382f0f775cd6b98c5e65fe,0,48
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/issue_bug_repo_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issue_bug_repo_commits.csv
new file mode 100644
index 000000000..959428ba5
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issue_bug_repo_commits.csv
@@ -0,0 +1,2 @@
+issue_id,repo_url,commit_sha,host,namespace,repo_name
+zentao:ZentaoBugRepoCommit:1:6094,http://devlake.gitlab.com/root/zentao-test,9ed3c33883a203e0cc90c44be40d105b23d98156,devlake.gitlab.com,root,zentao-test
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/issue_story_repo_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issue_story_repo_commits.csv
new file mode 100644
index 000000000..315b330dd
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issue_story_repo_commits.csv
@@ -0,0 +1,2 @@
+issue_id,repo_url,commit_sha,host,namespace,repo_name
+zentao:ZentaoStoryRepoCommit:1:4564,http://devlake.gitlab.com/root/zentao-test,659ca323434f22ed01145de9d52eeb0d8288d8bb,devlake.gitlab.com,root,zentao-test
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/issue_task_repo_commits.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issue_task_repo_commits.csv
new file mode 100644
index 000000000..0e971b834
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issue_task_repo_commits.csv
@@ -0,0 +1,2 @@
+issue_id,repo_url,commit_sha,host,namespace,repo_name
+zentao:ZentaoTaskRepoCommit:1:135,http://devlake.gitlab.com/root/zentao-test,ad92faa87e6eaf121d382f0f775cd6b98c5e65fe,devlake.gitlab.com,root,zentao-test
diff --git a/backend/plugins/zentao/e2e/story_commits_test.go
b/backend/plugins/zentao/e2e/story_commits_test.go
new file mode 100644
index 000000000..64fa9bdf5
--- /dev/null
+++ b/backend/plugins/zentao/e2e/story_commits_test.go
@@ -0,0 +1,76 @@
+/*
+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/core/models/common"
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/zentao/impl"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+ "github.com/apache/incubator-devlake/plugins/zentao/tasks"
+)
+
+func TestZentaoStoryCommitsDataFlow(t *testing.T) {
+
+ var zentao impl.Zentao
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao)
+
+ taskData := &tasks.ZentaoTaskData{
+ Options: &tasks.ZentaoOptions{
+ ConnectionId: 1,
+ ProjectId: 0,
+ ProductId: 31,
+ },
+ }
+
+ // import _raw_zentao_api_story_commits raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_story_commits.csv",
+ "_raw_zentao_api_story_commits")
+
+ // verify story commit extraction
+ dataflowTester.FlushTabler(&models.ZentaoStoryCommit{})
+ dataflowTester.Subtask(tasks.ExtractStoryCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&models.ZentaoStoryCommit{},
e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_zentao_story_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+ // import _raw_zentao_api_story_repo_commits raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_story_repo_commits.csv",
+ "_raw_zentao_api_story_repo_commits")
+
+ // verify story repo commit extraction
+ dataflowTester.FlushTabler(&models.ZentaoStoryRepoCommit{})
+ dataflowTester.Subtask(tasks.ExtractStoryRepoCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&models.ZentaoStoryRepoCommit{},
e2ehelper.TableOptions{
+ CSVRelPath:
"./snapshot_tables/_tool_zentao_story_repo_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+ // verify story repo commit conversion
+ dataflowTester.FlushTabler(&crossdomain.IssueRepoCommit{})
+ dataflowTester.Subtask(tasks.ConvertStoryRepoCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&crossdomain.IssueRepoCommit{},
e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_story_repo_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+}
diff --git a/backend/plugins/zentao/e2e/task_commits_test.go
b/backend/plugins/zentao/e2e/task_commits_test.go
new file mode 100644
index 000000000..478857f5e
--- /dev/null
+++ b/backend/plugins/zentao/e2e/task_commits_test.go
@@ -0,0 +1,76 @@
+/*
+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/core/models/common"
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/zentao/impl"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+ "github.com/apache/incubator-devlake/plugins/zentao/tasks"
+)
+
+func TestZentaoTaskCommitsDataFlow(t *testing.T) {
+
+ var zentao impl.Zentao
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao)
+
+ taskData := &tasks.ZentaoTaskData{
+ Options: &tasks.ZentaoOptions{
+ ConnectionId: 1,
+ ProjectId: 48,
+ ProductId: 0,
+ },
+ }
+
+ // import _raw_zentao_api_task_commits raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_task_commits.csv",
+ "_raw_zentao_api_task_commits")
+
+ // verify task commit extraction
+ dataflowTester.FlushTabler(&models.ZentaoTaskCommit{})
+ dataflowTester.Subtask(tasks.ExtractTaskCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&models.ZentaoTaskCommit{},
e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/_tool_zentao_task_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+ // import _raw_zentao_api_task_repo_commits raw data table
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_task_repo_commits.csv",
+ "_raw_zentao_api_task_repo_commits")
+
+ // verify task repo commit extraction
+ dataflowTester.FlushTabler(&models.ZentaoTaskRepoCommit{})
+ dataflowTester.Subtask(tasks.ExtractTaskRepoCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&models.ZentaoTaskRepoCommit{},
e2ehelper.TableOptions{
+ CSVRelPath:
"./snapshot_tables/_tool_zentao_task_repo_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+ // verify task repo commit conversion
+ dataflowTester.FlushTabler(&crossdomain.IssueRepoCommit{})
+ dataflowTester.Subtask(tasks.ConvertTaskRepoCommitsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&crossdomain.IssueRepoCommit{},
e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_task_repo_commits.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+
+}
diff --git a/backend/plugins/zentao/impl/impl.go
b/backend/plugins/zentao/impl/impl.go
index d10eb47ec..922a0456b 100644
--- a/backend/plugins/zentao/impl/impl.go
+++ b/backend/plugins/zentao/impl/impl.go
@@ -85,7 +85,24 @@ func (p Zentao) SubTaskMetas() []plugin.SubTaskMeta {
tasks.CollectDepartmentMeta,
tasks.ExtractDepartmentMeta,
tasks.ConvertDepartmentMeta,
+
tasks.CollectBugCommitsMeta,
+ tasks.ExtractBugCommitsMeta,
+ tasks.CollectBugRepoCommitsMeta,
+ tasks.ExtractBugRepoCommitsMeta,
+ tasks.ConvertBugRepoCommitsMeta,
+
+ tasks.CollectStoryCommitsMeta,
+ tasks.ExtractStoryCommitsMeta,
+ tasks.CollectStoryRepoCommitsMeta,
+ tasks.ExtractStoryRepoCommitsMeta,
+ tasks.ConvertStoryRepoCommitsMeta,
+
+ tasks.CollectTaskCommitsMeta,
+ tasks.ExtractTaskCommitsMeta,
+ tasks.CollectTaskRepoCommitsMeta,
+ tasks.ExtractTaskRepoCommitsMeta,
+ tasks.ConvertTaskRepoCommitsMeta,
}
}
diff --git a/backend/plugins/zentao/models/archived/bug_commits.go
b/backend/plugins/zentao/models/archived/bug_commits.go
index 21a2ad090..7bd02afe7 100644
--- a/backend/plugins/zentao/models/archived/bug_commits.go
+++ b/backend/plugins/zentao/models/archived/bug_commits.go
@@ -21,15 +21,42 @@ import (
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
)
-type ZentaoBugCommits struct {
+type ZentaoBugCommit struct {
archived.NoPKModel
- ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
- ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
- Project int64 `json:"project"`
- Product int64 `json:"product"`
- Actions []string `gorm:"type:json;serializer:json" json:"actions"
mapstructure:"actions"`
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+ ActionRead string `json:"actionRead"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ ActionDesc string `json:"cctionDesc"`
}
-func (ZentaoBugCommits) TableName() string {
+func (ZentaoBugCommit) TableName() string {
return "_tool_zentao_bug_commits"
}
+
+type ZentaoBugRepoCommit struct {
+ archived.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ IssueId string `gorm:"primaryKey;type:varchar(255)"` // the bug id
+ RepoUrl string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(255)"`
+}
+
+func (ZentaoBugRepoCommit) TableName() string {
+ return "_tool_zentao_bug_repo_commits"
+}
diff --git a/backend/plugins/zentao/models/archived/story_commits.go
b/backend/plugins/zentao/models/archived/story_commits.go
new file mode 100644
index 000000000..e4dc4d10f
--- /dev/null
+++ b/backend/plugins/zentao/models/archived/story_commits.go
@@ -0,0 +1,62 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package archived
+
+import (
+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+)
+
+type ZentaoStoryCommit struct {
+ archived.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+ ActionRead string `json:"actionRead"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ ActionDesc string `json:"cctionDesc"`
+}
+
+func (ZentaoStoryCommit) TableName() string {
+ return "_tool_zentao_story_commits"
+}
+
+type ZentaoStoryRepoCommit struct {
+ archived.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ IssueId string `gorm:"primaryKey;type:varchar(255)"` // the story
id
+ RepoUrl string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(255)"`
+}
+
+func (ZentaoStoryRepoCommit) TableName() string {
+ return "_tool_zentao_story_repo_commits"
+}
diff --git a/backend/plugins/zentao/models/archived/task_commits.go
b/backend/plugins/zentao/models/archived/task_commits.go
new file mode 100644
index 000000000..198e0fef7
--- /dev/null
+++ b/backend/plugins/zentao/models/archived/task_commits.go
@@ -0,0 +1,62 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package archived
+
+import (
+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+)
+
+type ZentaoTaskCommit struct {
+ archived.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+ ActionRead string `json:"actionRead"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ ActionDesc string `json:"cctionDesc"`
+}
+
+func (ZentaoTaskCommit) TableName() string {
+ return "_tool_zentao_task_commits"
+}
+
+type ZentaoTaskRepoCommit struct {
+ archived.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ IssueId string `gorm:"primaryKey;type:varchar(255)"` // the task id
+ RepoUrl string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(255)"`
+}
+
+func (ZentaoTaskRepoCommit) TableName() string {
+ return "_tool_zentao_task_repo_commits"
+}
diff --git a/backend/plugins/zentao/models/bug_commits.go
b/backend/plugins/zentao/models/bug_commits.go
index 918b8fe06..ce78cfde4 100644
--- a/backend/plugins/zentao/models/bug_commits.go
+++ b/backend/plugins/zentao/models/bug_commits.go
@@ -22,21 +22,130 @@ import (
)
type ZentaoBugCommitsRes struct {
- ID int64 `json:"id"`
- Project int64 `json:"project"`
- Product int64 `json:"product"`
- Actions []string `gorm:"type:json;serializer:json" json:"actions"
mapstructure:"actions"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product string `json:"product"`
+ Project int `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Read string `json:"read"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ Desc string `json:"desc"`
}
-type ZentaoBugCommits struct {
+type ZentaoBugCommit struct {
common.NoPKModel
- ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
- ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
- Project int64 `json:"project"`
- Product int64 `json:"product"`
- Actions []string `gorm:"type:json;serializer:json" json:"actions"
mapstructure:"actions"`
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+ ActionRead string `json:"actionRead"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ ActionDesc string `json:"cctionDesc"`
}
-func (ZentaoBugCommits) TableName() string {
+func (ZentaoBugCommit) TableName() string {
return "_tool_zentao_bug_commits"
}
+
+type ZentaoBugRepoCommitsRes struct {
+ Title string `json:"title"`
+ Log struct {
+ Revision string `json:"revision"`
+ Committer string `json:"committer"`
+ Time string `json:"time"`
+ Comment string `json:"comment"`
+ Change struct {
+ TestYaml struct {
+ Action string `json:"action"`
+ Kind string `json:"kind"`
+ OldPath string `json:"oldPath"`
+ } `json:"/test.yaml"`
+ } `json:"change"`
+ Commit string `json:"commit"`
+ } `json:"log"`
+ Repo struct {
+ ID string `json:"id"`
+ Product string `json:"product"`
+ Projects string `json:"projects"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ Prefix string `json:"prefix"`
+ Encoding string `json:"encoding"`
+ Scm string `json:"SCM"`
+ Client string `json:"client"`
+ ServiceHost string `json:"serviceHost"`
+ ServiceProject string `json:"serviceProject"`
+ Commits string `json:"commits"`
+ Account string `json:"account"`
+ Password string `json:"password"`
+ Encrypt string `json:"encrypt"`
+ ACL any `json:"acl"`
+ Synced string `json:"synced"`
+ LastSync string `json:"lastSync"`
+ Desc string `json:"desc"`
+ Extra string `json:"extra"`
+ PreMerge string `json:"preMerge"`
+ Job string `json:"job"`
+ FileServerURL any `json:"fileServerUrl"`
+ FileServerAccount string `json:"fileServerAccount"`
+ FileServerPassword string `json:"fileServerPassword"`
+ Deleted string `json:"deleted"`
+ CodePath string `json:"codePath"`
+ GitService string `json:"gitService"`
+ Project string `json:"project"`
+ } `json:"repo"`
+ Path string `json:"path"`
+ Type string `json:"type"`
+ Changes struct {
+ TestYaml struct {
+ Action string `json:"action"`
+ Kind string `json:"kind"`
+ OldPath string `json:"oldPath"`
+ View string `json:"view"`
+ Diff string `json:"diff"`
+ } `json:"/test.yaml"`
+ } `json:"changes"`
+ RepoID string `json:"repoID"`
+ BranchID bool `json:"branchID"`
+ ObjectID string `json:"objectID"`
+ Revision string `json:"revision"`
+ ParentDir string `json:"parentDir"`
+ OldRevision string `json:"oldRevision"`
+ PreAndNext struct {
+ Pre string `json:"pre"`
+ Next string `json:"next"`
+ } `json:"preAndNext"`
+ Pager any `json:"pager"`
+}
+
+type ZentaoBugRepoCommit struct {
+ common.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ IssueId string `gorm:"primaryKey;type:varchar(255)"` // the bug id
+ RepoUrl string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(255)"`
+}
+
+func (ZentaoBugRepoCommit) TableName() string {
+ return "_tool_zentao_bug_repo_commits"
+}
diff --git
a/backend/plugins/zentao/models/migrationscripts/20230531_add_issue_commits.go
b/backend/plugins/zentao/models/migrationscripts/20230605_add_issue_repo_commits.go
similarity index 69%
rename from
backend/plugins/zentao/models/migrationscripts/20230531_add_issue_commits.go
rename to
backend/plugins/zentao/models/migrationscripts/20230605_add_issue_repo_commits.go
index 75b6cb606..a50cb4aba 100644
---
a/backend/plugins/zentao/models/migrationscripts/20230531_add_issue_commits.go
+++
b/backend/plugins/zentao/models/migrationscripts/20230605_add_issue_repo_commits.go
@@ -24,20 +24,25 @@ import (
"github.com/apache/incubator-devlake/plugins/zentao/models/archived"
)
-type addIssueCommitsTables struct{}
+type addIssueRepoCommitsTables struct{}
-func (*addIssueCommitsTables) Up(basicRes context.BasicRes) errors.Error {
+func (*addIssueRepoCommitsTables) Up(basicRes context.BasicRes) errors.Error {
return migrationhelper.AutoMigrateTables(
basicRes,
- &archived.ZentaoBugCommits{},
+ &archived.ZentaoBugCommit{},
+ &archived.ZentaoBugRepoCommit{},
+ &archived.ZentaoStoryCommit{},
+ &archived.ZentaoStoryRepoCommit{},
+ &archived.ZentaoTaskCommit{},
+ &archived.ZentaoTaskRepoCommit{},
)
}
-func (*addIssueCommitsTables) Version() uint64 {
- return 20230531000001
+func (*addIssueRepoCommitsTables) Version() uint64 {
+ return 20230605000011
}
-func (*addIssueCommitsTables) Name() string {
- return "zentao add issue commits tables"
+func (*addIssueRepoCommitsTables) Name() string {
+ return "zentao add issue repo commits tables"
}
diff --git a/backend/plugins/zentao/models/migrationscripts/register.go
b/backend/plugins/zentao/models/migrationscripts/register.go
index df08cb6a2..9ce9af76c 100644
--- a/backend/plugins/zentao/models/migrationscripts/register.go
+++ b/backend/plugins/zentao/models/migrationscripts/register.go
@@ -26,7 +26,7 @@ func All() []plugin.MigrationScript {
return []plugin.MigrationScript{
new(addInitTables),
new(addInitChangelogTables),
- new(addIssueCommitsTables),
new(addScopeConfigTables),
+ new(addIssueRepoCommitsTables),
}
}
diff --git a/backend/plugins/zentao/models/story_commits.go
b/backend/plugins/zentao/models/story_commits.go
new file mode 100644
index 000000000..937738149
--- /dev/null
+++ b/backend/plugins/zentao/models/story_commits.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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+)
+
+type ZentaoStoryCommitsRes struct {
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product string `json:"product"`
+ Project int `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Read string `json:"read"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ Desc string `json:"desc"`
+}
+
+type ZentaoStoryCommit struct {
+ common.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+ ActionRead string `json:"actionRead"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ ActionDesc string `json:"cctionDesc"`
+}
+
+func (ZentaoStoryCommit) TableName() string {
+ return "_tool_zentao_story_commits"
+}
+
+type ZentaoStoryRepoCommitsRes struct {
+ Title string `json:"title"`
+ Log struct {
+ Revision string `json:"revision"`
+ Committer string `json:"committer"`
+ Time string `json:"time"`
+ Comment string `json:"comment"`
+ Change struct {
+ TestYaml struct {
+ Action string `json:"action"`
+ Kind string `json:"kind"`
+ OldPath string `json:"oldPath"`
+ } `json:"/test.yaml"`
+ } `json:"change"`
+ Commit string `json:"commit"`
+ } `json:"log"`
+ Repo struct {
+ ID string `json:"id"`
+ Product string `json:"product"`
+ Projects string `json:"projects"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ Prefix string `json:"prefix"`
+ Encoding string `json:"encoding"`
+ Scm string `json:"SCM"`
+ Client string `json:"client"`
+ ServiceHost string `json:"serviceHost"`
+ ServiceProject string `json:"serviceProject"`
+ Commits string `json:"commits"`
+ Account string `json:"account"`
+ Password string `json:"password"`
+ Encrypt string `json:"encrypt"`
+ ACL any `json:"acl"`
+ Synced string `json:"synced"`
+ LastSync string `json:"lastSync"`
+ Desc string `json:"desc"`
+ Extra string `json:"extra"`
+ PreMerge string `json:"preMerge"`
+ Job string `json:"job"`
+ FileServerURL any `json:"fileServerUrl"`
+ FileServerAccount string `json:"fileServerAccount"`
+ FileServerPassword string `json:"fileServerPassword"`
+ Deleted string `json:"deleted"`
+ CodePath string `json:"codePath"`
+ GitService string `json:"gitService"`
+ Project string `json:"project"`
+ } `json:"repo"`
+ Path string `json:"path"`
+ Type string `json:"type"`
+ Changes struct {
+ TestYaml struct {
+ Action string `json:"action"`
+ Kind string `json:"kind"`
+ OldPath string `json:"oldPath"`
+ View string `json:"view"`
+ Diff string `json:"diff"`
+ } `json:"/test.yaml"`
+ } `json:"changes"`
+ RepoID string `json:"repoID"`
+ BranchID bool `json:"branchID"`
+ ObjectID string `json:"objectID"`
+ Revision string `json:"revision"`
+ ParentDir string `json:"parentDir"`
+ OldRevision string `json:"oldRevision"`
+ PreAndNext struct {
+ Pre string `json:"pre"`
+ Next string `json:"next"`
+ } `json:"preAndNext"`
+ Pager any `json:"pager"`
+}
+
+type ZentaoStoryRepoCommit struct {
+ common.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ IssueId string `gorm:"primaryKey;type:varchar(255)"` // the story
id
+ RepoUrl string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(255)"`
+}
+
+func (ZentaoStoryRepoCommit) TableName() string {
+ return "_tool_zentao_story_repo_commits"
+}
diff --git a/backend/plugins/zentao/models/task_commits.go
b/backend/plugins/zentao/models/task_commits.go
new file mode 100644
index 000000000..f297358b5
--- /dev/null
+++ b/backend/plugins/zentao/models/task_commits.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 models
+
+import (
+ "github.com/apache/incubator-devlake/core/models/common"
+)
+
+type ZentaoTaskCommitsRes struct {
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product string `json:"product"`
+ Project int `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Read string `json:"read"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ Desc string `json:"desc"`
+}
+
+type ZentaoTaskCommit struct {
+ common.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ ID int `json:"id" gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false"`
+ ObjectType string `json:"objectType"`
+ ObjectID int `json:"objectID"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ Execution int `json:"execution"`
+ Actor string `json:"actor"`
+ Action string `json:"action"`
+ Date string `json:"date"`
+ Comment string `json:"comment"`
+ Extra string `json:"extra"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+ ActionRead string `json:"actionRead"`
+ Vision string `json:"vision"`
+ Efforted int `json:"efforted"`
+ ActionDesc string `json:"cctionDesc"`
+}
+
+func (ZentaoTaskCommit) TableName() string {
+ return "_tool_zentao_task_commits"
+}
+
+type ZentaoTaskRepoCommitsRes struct {
+ Title string `json:"title"`
+ Log struct {
+ Revision string `json:"revision"`
+ Committer string `json:"committer"`
+ Time string `json:"time"`
+ Comment string `json:"comment"`
+ Change struct {
+ TestYaml struct {
+ Action string `json:"action"`
+ Kind string `json:"kind"`
+ OldPath string `json:"oldPath"`
+ } `json:"/test.yaml"`
+ } `json:"change"`
+ Commit string `json:"commit"`
+ } `json:"log"`
+ Repo struct {
+ ID string `json:"id"`
+ Product string `json:"product"`
+ Projects string `json:"projects"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ Prefix string `json:"prefix"`
+ Encoding string `json:"encoding"`
+ Scm string `json:"SCM"`
+ Client string `json:"client"`
+ ServiceHost string `json:"serviceHost"`
+ ServiceProject string `json:"serviceProject"`
+ Commits string `json:"commits"`
+ Account string `json:"account"`
+ Password string `json:"password"`
+ Encrypt string `json:"encrypt"`
+ ACL any `json:"acl"`
+ Synced string `json:"synced"`
+ LastSync string `json:"lastSync"`
+ Desc string `json:"desc"`
+ Extra string `json:"extra"`
+ PreMerge string `json:"preMerge"`
+ Job string `json:"job"`
+ FileServerURL any `json:"fileServerUrl"`
+ FileServerAccount string `json:"fileServerAccount"`
+ FileServerPassword string `json:"fileServerPassword"`
+ Deleted string `json:"deleted"`
+ CodePath string `json:"codePath"`
+ GitService string `json:"gitService"`
+ Project string `json:"project"`
+ } `json:"repo"`
+ Path string `json:"path"`
+ Type string `json:"type"`
+ Changes struct {
+ TestYaml struct {
+ Action string `json:"action"`
+ Kind string `json:"kind"`
+ OldPath string `json:"oldPath"`
+ View string `json:"view"`
+ Diff string `json:"diff"`
+ } `json:"/test.yaml"`
+ } `json:"changes"`
+ RepoID string `json:"repoID"`
+ BranchID bool `json:"branchID"`
+ ObjectID string `json:"objectID"`
+ Revision string `json:"revision"`
+ ParentDir string `json:"parentDir"`
+ OldRevision string `json:"oldRevision"`
+ PreAndNext struct {
+ Pre string `json:"pre"`
+ Next string `json:"next"`
+ } `json:"preAndNext"`
+ Pager any `json:"pager"`
+}
+
+type ZentaoTaskRepoCommit struct {
+ common.NoPKModel
+ ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Product int64 `json:"product"`
+ Project int64 `json:"project"`
+ IssueId string `gorm:"primaryKey;type:varchar(255)"` // the task id
+ RepoUrl string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(255)"`
+}
+
+func (ZentaoTaskRepoCommit) TableName() string {
+ return "_tool_zentao_task_repo_commits"
+}
diff --git a/backend/plugins/zentao/tasks/bug_commits_collector.go
b/backend/plugins/zentao/tasks/bug_commits_collector.go
index 639885f29..de3f32232 100644
--- a/backend/plugins/zentao/tasks/bug_commits_collector.go
+++ b/backend/plugins/zentao/tasks/bug_commits_collector.go
@@ -19,10 +19,7 @@ package tasks
import (
"encoding/json"
- "fmt"
- "io"
"net/http"
- "net/url"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -54,6 +51,7 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
Table: RAW_BUG_COMMITS_TABLE,
}, data.TimeAfter)
@@ -63,7 +61,7 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
// load bugs id from db
clauses := []dal.Clause{
- dal.Select("id"),
+ dal.Select("id, last_edited_date"),
dal.From(&models.ZentaoBug{}),
dal.Where(
"product = ? AND connection_id = ?",
@@ -75,7 +73,7 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
if incremental {
clauses = append(
clauses,
- dal.Where("updated_at > ?",
collectorWithState.LatestState.LatestSuccessStart),
+ dal.Where("last_edited_date is not null and
last_edited_date > ?", collectorWithState.LatestState.LatestSuccessStart),
)
}
cursor, err := db.Cursor(clauses...)
@@ -93,6 +91,7 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
Table: RAW_BUG_COMMITS_TABLE,
},
@@ -101,21 +100,18 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
Input: iterator,
Incremental: incremental,
UrlTemplate: "bugs/{{ .Input.ID }}",
- Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("per_page", fmt.Sprintf("%v",
reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- body, err := io.ReadAll(res.Body)
+ var data struct {
+ Actions []json.RawMessage `json:"actions"`
+ }
+ err := api.UnmarshalResponse(res, &data)
if err != nil {
- return nil, errors.Convert(err)
+ return nil, err
}
- res.Body.Close()
- return []json.RawMessage{body}, nil
+ return data.Actions, nil
+
},
+ AfterResponse: ignoreHTTPStatus404,
})
if err != nil {
return err
diff --git a/backend/plugins/zentao/tasks/bug_commits_extractor.go
b/backend/plugins/zentao/tasks/bug_commits_extractor.go
new file mode 100644
index 000000000..8492810a2
--- /dev/null
+++ b/backend/plugins/zentao/tasks/bug_commits_extractor.go
@@ -0,0 +1,113 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "net/url"
+ "regexp"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractBugCommits
+
+var ExtractBugCommitsMeta = plugin.SubTaskMeta{
+ Name: "extractBugCommits",
+ EntryPoint: ExtractBugCommits,
+ EnabledByDefault: true,
+ Description: "extract Zentao bug commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractBugCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+
+ // this Extract only work for product
+ if data.Options.ProductId == 0 {
+ return nil
+ }
+
+ re := regexp.MustCompile(`href='(.*?)'`)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_BUG_COMMITS_TABLE,
+ },
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ res := &models.ZentaoBugCommitsRes{}
+ err := json.Unmarshal(row.Data, res)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+ // only linked2revision action is valid
+ if res.Action != "linked2revision" {
+ return nil, nil
+ }
+
+ bugCommits := &models.ZentaoBugCommit{
+ ConnectionId: data.Options.ConnectionId,
+ ID: res.ID,
+ ObjectType: res.ObjectType,
+ ObjectID: res.ObjectID,
+ Product: data.Options.ProductId,
+ Project: data.Options.ProjectId,
+ Execution: res.Execution,
+ Actor: res.Actor,
+ Action: res.Action,
+ Date: res.Date,
+ Comment: res.Comment,
+ ActionRead: res.Read,
+ Vision: res.Vision,
+ Efforted: res.Efforted,
+ ActionDesc: res.Desc,
+ }
+
+ match := re.FindStringSubmatch(res.Extra)
+ if len(match) > 1 {
+ bugCommits.Extra = match[1]
+ } else {
+ return nil, nil
+ }
+ u, err := url.Parse(match[1])
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+ bugCommits.Host = u.Host
+ bugCommits.RepoRevision = u.Path
+
+ results := make([]interface{}, 0)
+ results = append(results, bugCommits)
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/bug_commits_collector.go
b/backend/plugins/zentao/tasks/bug_repo_commits_collector.go
similarity index 53%
copy from backend/plugins/zentao/tasks/bug_commits_collector.go
copy to backend/plugins/zentao/tasks/bug_repo_commits_collector.go
index 639885f29..c9f28ae02 100644
--- a/backend/plugins/zentao/tasks/bug_commits_collector.go
+++ b/backend/plugins/zentao/tasks/bug_repo_commits_collector.go
@@ -19,10 +19,7 @@ package tasks
import (
"encoding/json"
- "fmt"
- "io"
"net/http"
- "net/url"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -32,98 +29,84 @@ import (
"github.com/apache/incubator-devlake/plugins/zentao/models"
)
-const RAW_BUG_COMMITS_TABLE = "zentao_api_bug_commits"
+const RAW_BUG_REPO_COMMITS_TABLE = "zentao_api_bug_repo_commits"
-var _ plugin.SubTaskEntryPoint = CollectBugCommits
+var _ plugin.SubTaskEntryPoint = CollectBugRepoCommits
-var CollectBugCommitsMeta = plugin.SubTaskMeta{
- Name: "collectBugCommits",
- EntryPoint: CollectBugCommits,
+var CollectBugRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "collectBugRepoCommits",
+ EntryPoint: CollectBugRepoCommits,
EnabledByDefault: true,
- Description: "Collect Bug Commits data from Zentao api",
+ Description: "Collect Bug Repo Commits data from Zentao api",
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
}
-func CollectBugCommits(taskCtx plugin.SubTaskContext) errors.Error {
+func CollectBugRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
db := taskCtx.GetDal()
data := taskCtx.GetData().(*ZentaoTaskData)
- // state manager
- collectorWithState, err :=
api.NewStatefulApiCollector(api.RawDataSubTaskArgs{
- Ctx: taskCtx,
- Params: ZentaoApiParams{
- ConnectionId: data.Options.ConnectionId,
- ProductId: data.Options.ProductId,
- },
- Table: RAW_BUG_COMMITS_TABLE,
- }, data.TimeAfter)
- if err != nil {
- return err
- }
-
// load bugs id from db
clauses := []dal.Clause{
- dal.Select("id"),
- dal.From(&models.ZentaoBug{}),
+ dal.Select("object_id, repo_revision"),
+ dal.From(&models.ZentaoBugCommit{}),
dal.Where(
"product = ? AND connection_id = ?",
data.Options.ProductId, data.Options.ConnectionId,
),
}
- // incremental collection
- incremental := collectorWithState.IsIncremental()
- if incremental {
- clauses = append(
- clauses,
- dal.Where("updated_at > ?",
collectorWithState.LatestState.LatestSuccessStart),
- )
- }
+
cursor, err := db.Cursor(clauses...)
if err != nil {
return err
}
- iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoBug{}))
+
+ iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoBugCommit{}))
if err != nil {
return err
}
- // collect bug commits
- err = collectorWithState.InitCollector(api.ApiCollectorArgs{
+
+ // collect bug repo commits
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_BUG_REPO_COMMITS_TABLE,
},
ApiClient: data.ApiClient,
- PageSize: 100,
Input: iterator,
- Incremental: incremental,
- UrlTemplate: "bugs/{{ .Input.ID }}",
- Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("per_page", fmt.Sprintf("%v",
reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
+ UrlTemplate: "../..{{ .Input.RepoRevision }}",
ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- body, err := io.ReadAll(res.Body)
+ var result RepoRevisionResponse
+ err := api.UnmarshalResponse(res, &result)
if err != nil {
- return nil, errors.Convert(err)
+ return nil, err
}
- res.Body.Close()
- return []json.RawMessage{body}, nil
+ byteData := []byte(result.Data)
+ return []json.RawMessage{byteData}, nil
+
},
+ AfterResponse: ignoreHTTPStatus404,
})
if err != nil {
return err
}
- return collectorWithState.Execute()
+ return collector.Execute()
+}
+
+type SimpleZentaoBugCommit struct {
+ ObjectID int `json:"objectID"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+
}
-type SimpleZentaoBug struct {
- ID int64 `json:"id"`
+type RepoRevisionResponse struct {
+ Status string `json:"status"`
+ Data string `json:"data"`
+ MD5 string `json:"md5"`
}
diff --git a/backend/plugins/zentao/tasks/bug_repo_commits_convertor.go
b/backend/plugins/zentao/tasks/bug_repo_commits_convertor.go
new file mode 100644
index 000000000..f425f2c76
--- /dev/null
+++ b/backend/plugins/zentao/tasks/bug_repo_commits_convertor.go
@@ -0,0 +1,93 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ConvertBugRepoCommits
+
+var ConvertBugRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "convertBugRepoCommits",
+ EntryPoint: ConvertBugRepoCommits,
+ EnabledByDefault: true,
+ Description: "convert Zentao bug repo commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertBugRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+ db := taskCtx.GetDal()
+
+ cursor, err := db.Cursor(
+ dal.From(&models.ZentaoBugRepoCommit{}),
+ dal.Where(`product = ? and connection_id = ?`,
data.Options.ProductId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ issueIdGenerator :=
didgen.NewDomainIdGenerator(&models.ZentaoBugRepoCommit{})
+ convertor, err := api.NewDataConverter(api.DataConverterArgs{
+ InputRowType: reflect.TypeOf(models.ZentaoBugRepoCommit{}),
+ Input: cursor,
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_BUG_REPO_COMMITS_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
+ toolEntity := inputRow.(*models.ZentaoBugRepoCommit)
+ domainEntity := &crossdomain.IssueRepoCommit{
+ IssueId:
issueIdGenerator.Generate(data.Options.ConnectionId, toolEntity.IssueId),
+ RepoUrl: toolEntity.RepoUrl,
+ CommitSha: toolEntity.CommitSha,
+ }
+ host, namespace, repoName, err :=
parseRepoUrl(domainEntity.RepoUrl)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+ domainEntity.Host = host
+ domainEntity.Namespace = namespace
+ domainEntity.RepoName = repoName
+
+ var results []interface{}
+ results = append(results, domainEntity)
+ return results, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return convertor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/bug_repo_commits_extractor.go
b/backend/plugins/zentao/tasks/bug_repo_commits_extractor.go
new file mode 100644
index 000000000..292416bd4
--- /dev/null
+++ b/backend/plugins/zentao/tasks/bug_repo_commits_extractor.go
@@ -0,0 +1,91 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "regexp"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractBugRepoCommits
+
+var ExtractBugRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "extractBugRepoCommits",
+ EntryPoint: ExtractBugRepoCommits,
+ EnabledByDefault: true,
+ Description: "extract Zentao bug repo commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractBugRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+
+ // this Extract only work for product
+ if data.Options.ProductId == 0 {
+ return nil
+ }
+ re := regexp.MustCompile(`(\d+)(?:,\s*(\d+))*`)
+
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_BUG_REPO_COMMITS_TABLE,
+ },
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ res := &models.ZentaoBugRepoCommitsRes{}
+ err := json.Unmarshal(row.Data, res)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ results := make([]interface{}, 0)
+ match := re.FindStringSubmatch(res.Log.Comment)
+ for i := 1; i < len(match); i++ {
+ if match[i] != "" {
+ bugRepoCommits :=
&models.ZentaoBugRepoCommit{
+ ConnectionId:
data.Options.ConnectionId,
+ Product:
data.Options.ProductId,
+ Project:
data.Options.ProjectId,
+ RepoUrl: res.Repo.CodePath,
+ CommitSha: res.Revision,
+ IssueId: match[i], // bug
id
+ }
+ results = append(results,
bugRepoCommits)
+ }
+ }
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/shared.go
b/backend/plugins/zentao/tasks/shared.go
index d9fcc69c4..3c16b347e 100644
--- a/backend/plugins/zentao/tasks/shared.go
+++ b/backend/plugins/zentao/tasks/shared.go
@@ -18,7 +18,9 @@ limitations under the License.
package tasks
import (
+ "fmt"
"net/http"
+ "net/url"
"strings"
"github.com/apache/incubator-devlake/core/errors"
@@ -142,3 +144,36 @@ func getStdTypeMappings(data *ZentaoTaskData)
map[string]string {
}
return stdTypeMappings
}
+
+// parseRepoUrl parses a repository URL and returns the host, namespace, and
repository name.
+func parseRepoUrl(repoUrl string) (string, string, string, error) {
+ parsedUrl, err := url.Parse(repoUrl)
+ if err != nil {
+ return "", "", "", err
+ }
+
+ host := parsedUrl.Host
+ host = strings.TrimPrefix(host, "www.")
+ pathParts := strings.Split(parsedUrl.Path, "/")
+ if len(pathParts) < 3 {
+ return "", "", "", fmt.Errorf("invalid RepoUrl: %s", repoUrl)
+ }
+
+ namespace := strings.Join(pathParts[1:len(pathParts)-1], "/")
+ repoName := pathParts[len(pathParts)-1]
+ if repoName == "" {
+ return "", "", "", fmt.Errorf("invalid RepoUrl: %s (empty
repository name)", repoUrl)
+ }
+
+ return host, namespace, repoName, nil
+}
+
+func ignoreHTTPStatus404(res *http.Response) errors.Error {
+ if res.StatusCode == http.StatusUnauthorized {
+ return errors.Unauthorized.New("authentication failed, please
check your AccessToken")
+ }
+ if res.StatusCode == http.StatusNotFound {
+ return api.ErrIgnoreAndContinue
+ }
+ return nil
+}
diff --git a/backend/plugins/zentao/tasks/bug_commits_collector.go
b/backend/plugins/zentao/tasks/story_commits_collector.go
similarity index 68%
copy from backend/plugins/zentao/tasks/bug_commits_collector.go
copy to backend/plugins/zentao/tasks/story_commits_collector.go
index 639885f29..8f3cc5772 100644
--- a/backend/plugins/zentao/tasks/bug_commits_collector.go
+++ b/backend/plugins/zentao/tasks/story_commits_collector.go
@@ -19,10 +19,7 @@ package tasks
import (
"encoding/json"
- "fmt"
- "io"
"net/http"
- "net/url"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -32,19 +29,19 @@ import (
"github.com/apache/incubator-devlake/plugins/zentao/models"
)
-const RAW_BUG_COMMITS_TABLE = "zentao_api_bug_commits"
+const RAW_STORY_COMMITS_TABLE = "zentao_api_story_commits"
-var _ plugin.SubTaskEntryPoint = CollectBugCommits
+var _ plugin.SubTaskEntryPoint = CollectStoryCommits
-var CollectBugCommitsMeta = plugin.SubTaskMeta{
- Name: "collectBugCommits",
- EntryPoint: CollectBugCommits,
+var CollectStoryCommitsMeta = plugin.SubTaskMeta{
+ Name: "collectStoryCommits",
+ EntryPoint: CollectStoryCommits,
EnabledByDefault: true,
- Description: "Collect Bug Commits data from Zentao api",
+ Description: "Collect Story Commits data from Zentao api",
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
}
-func CollectBugCommits(taskCtx plugin.SubTaskContext) errors.Error {
+func CollectStoryCommits(taskCtx plugin.SubTaskContext) errors.Error {
db := taskCtx.GetDal()
data := taskCtx.GetData().(*ZentaoTaskData)
@@ -54,17 +51,18 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_STORY_COMMITS_TABLE,
}, data.TimeAfter)
if err != nil {
return err
}
- // load bugs id from db
+ // load stories id from db
clauses := []dal.Clause{
- dal.Select("id"),
- dal.From(&models.ZentaoBug{}),
+ dal.Select("id, last_edited_date"),
+ dal.From(&models.ZentaoStory{}),
dal.Where(
"product = ? AND connection_id = ?",
data.Options.ProductId, data.Options.ConnectionId,
@@ -75,47 +73,47 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
if incremental {
clauses = append(
clauses,
- dal.Where("updated_at > ?",
collectorWithState.LatestState.LatestSuccessStart),
+ dal.Where("last_edited_date is not null and
last_edited_date > ?", collectorWithState.LatestState.LatestSuccessStart),
)
}
cursor, err := db.Cursor(clauses...)
if err != nil {
return err
}
- iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoBug{}))
+
+ iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoStory{}))
if err != nil {
return err
}
- // collect bug commits
+
+ // collect story commits
err = collectorWithState.InitCollector(api.ApiCollectorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_STORY_COMMITS_TABLE,
},
ApiClient: data.ApiClient,
PageSize: 100,
Input: iterator,
Incremental: incremental,
- UrlTemplate: "bugs/{{ .Input.ID }}",
- Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("per_page", fmt.Sprintf("%v",
reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
+ UrlTemplate: "stories/{{ .Input.ID }}",
ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- body, err := io.ReadAll(res.Body)
+ var data struct {
+ Actions []json.RawMessage `json:"actions"`
+ }
+ err := api.UnmarshalResponse(res, &data)
if err != nil {
- return nil, errors.Convert(err)
+ return nil, err
}
- res.Body.Close()
- return []json.RawMessage{body}, nil
+ return data.Actions, nil
+
},
+ AfterResponse: ignoreHTTPStatus404,
})
if err != nil {
return err
@@ -124,6 +122,6 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
return collectorWithState.Execute()
}
-type SimpleZentaoBug struct {
+type SimpleZentaoStory struct {
ID int64 `json:"id"`
}
diff --git a/backend/plugins/zentao/tasks/story_commits_extractor.go
b/backend/plugins/zentao/tasks/story_commits_extractor.go
new file mode 100644
index 000000000..0e119c3e6
--- /dev/null
+++ b/backend/plugins/zentao/tasks/story_commits_extractor.go
@@ -0,0 +1,115 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "net/url"
+ "regexp"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractStoryCommits
+
+var ExtractStoryCommitsMeta = plugin.SubTaskMeta{
+ Name: "extractStoryCommits",
+ EntryPoint: ExtractStoryCommits,
+ EnabledByDefault: true,
+ Description: "extract Zentao story commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractStoryCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+
+ // this Extract only work for product
+ if data.Options.ProductId == 0 {
+ return nil
+ }
+
+ re := regexp.MustCompile(`href='(.*?)'`)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_STORY_COMMITS_TABLE,
+ },
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ res := &models.ZentaoStoryCommitsRes{}
+ err := json.Unmarshal(row.Data, res)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ // only linked2revision action is valid
+ if res.Action != "linked2revision" {
+ return nil, nil
+ }
+
+ storyCommits := &models.ZentaoStoryCommit{
+ ConnectionId: data.Options.ConnectionId,
+ ID: res.ID,
+ ObjectType: res.ObjectType,
+ ObjectID: res.ObjectID,
+ Product: data.Options.ProductId,
+ Project: data.Options.ProjectId,
+ Execution: res.Execution,
+ Actor: res.Actor,
+ Action: res.Action,
+ Date: res.Date,
+ Comment: res.Comment,
+ ActionRead: res.Read,
+ Vision: res.Vision,
+ Efforted: res.Efforted,
+ ActionDesc: res.Desc,
+ }
+
+ match := re.FindStringSubmatch(res.Extra)
+ if len(match) > 1 {
+ storyCommits.Extra = match[1]
+ } else {
+ return nil, nil
+ }
+ u, err := url.Parse(match[1])
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ storyCommits.Host = u.Host
+ storyCommits.RepoRevision = u.Path
+
+ results := make([]interface{}, 0)
+ results = append(results, storyCommits)
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/bug_commits_collector.go
b/backend/plugins/zentao/tasks/story_repo_commits_collector.go
similarity index 53%
copy from backend/plugins/zentao/tasks/bug_commits_collector.go
copy to backend/plugins/zentao/tasks/story_repo_commits_collector.go
index 639885f29..ddcdbf47a 100644
--- a/backend/plugins/zentao/tasks/bug_commits_collector.go
+++ b/backend/plugins/zentao/tasks/story_repo_commits_collector.go
@@ -19,10 +19,7 @@ package tasks
import (
"encoding/json"
- "fmt"
- "io"
"net/http"
- "net/url"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -32,98 +29,78 @@ import (
"github.com/apache/incubator-devlake/plugins/zentao/models"
)
-const RAW_BUG_COMMITS_TABLE = "zentao_api_bug_commits"
+const RAW_STORY_REPO_COMMITS_TABLE = "zentao_api_story_repo_commits"
-var _ plugin.SubTaskEntryPoint = CollectBugCommits
+var _ plugin.SubTaskEntryPoint = CollectStoryRepoCommits
-var CollectBugCommitsMeta = plugin.SubTaskMeta{
- Name: "collectBugCommits",
- EntryPoint: CollectBugCommits,
+var CollectStoryRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "collectStoryRepoCommits",
+ EntryPoint: CollectStoryRepoCommits,
EnabledByDefault: true,
- Description: "Collect Bug Commits data from Zentao api",
+ Description: "Collect Story Repo Commits data from Zentao api",
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
}
-func CollectBugCommits(taskCtx plugin.SubTaskContext) errors.Error {
+func CollectStoryRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
db := taskCtx.GetDal()
data := taskCtx.GetData().(*ZentaoTaskData)
- // state manager
- collectorWithState, err :=
api.NewStatefulApiCollector(api.RawDataSubTaskArgs{
- Ctx: taskCtx,
- Params: ZentaoApiParams{
- ConnectionId: data.Options.ConnectionId,
- ProductId: data.Options.ProductId,
- },
- Table: RAW_BUG_COMMITS_TABLE,
- }, data.TimeAfter)
- if err != nil {
- return err
- }
-
- // load bugs id from db
+ // load stories id from db
clauses := []dal.Clause{
- dal.Select("id"),
- dal.From(&models.ZentaoBug{}),
+ dal.Select("object_id, repo_revision"),
+ dal.From(&models.ZentaoStoryCommit{}),
dal.Where(
"product = ? AND connection_id = ?",
data.Options.ProductId, data.Options.ConnectionId,
),
}
- // incremental collection
- incremental := collectorWithState.IsIncremental()
- if incremental {
- clauses = append(
- clauses,
- dal.Where("updated_at > ?",
collectorWithState.LatestState.LatestSuccessStart),
- )
- }
+
cursor, err := db.Cursor(clauses...)
if err != nil {
return err
}
- iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoBug{}))
+
+ iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoStoryCommit{}))
if err != nil {
return err
}
- // collect bug commits
- err = collectorWithState.InitCollector(api.ApiCollectorArgs{
+
+ // collect story repo commits
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_STORY_REPO_COMMITS_TABLE,
},
ApiClient: data.ApiClient,
- PageSize: 100,
Input: iterator,
- Incremental: incremental,
- UrlTemplate: "bugs/{{ .Input.ID }}",
- Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("per_page", fmt.Sprintf("%v",
reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
+ UrlTemplate: "../..{{ .Input.RepoRevision }}",
ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- body, err := io.ReadAll(res.Body)
+ var result RepoRevisionResponse
+ err := api.UnmarshalResponse(res, &result)
if err != nil {
- return nil, errors.Convert(err)
+ return nil, err
}
- res.Body.Close()
- return []json.RawMessage{body}, nil
+ byteData := []byte(result.Data)
+ return []json.RawMessage{byteData}, nil
+
},
+ AfterResponse: ignoreHTTPStatus404,
})
if err != nil {
return err
}
- return collectorWithState.Execute()
+ return collector.Execute()
}
-type SimpleZentaoBug struct {
- ID int64 `json:"id"`
+type SimpleZentaoStoryCommit struct {
+ ObjectID int `json:"objectID"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+
}
diff --git a/backend/plugins/zentao/tasks/story_repo_commits_convertor.go
b/backend/plugins/zentao/tasks/story_repo_commits_convertor.go
new file mode 100644
index 000000000..612a61929
--- /dev/null
+++ b/backend/plugins/zentao/tasks/story_repo_commits_convertor.go
@@ -0,0 +1,93 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ConvertStoryRepoCommits
+
+var ConvertStoryRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "convertStoryRepoCommits",
+ EntryPoint: ConvertStoryRepoCommits,
+ EnabledByDefault: true,
+ Description: "convert Zentao story repo commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertStoryRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+ db := taskCtx.GetDal()
+
+ cursor, err := db.Cursor(
+ dal.From(&models.ZentaoStoryRepoCommit{}),
+ dal.Where(`product = ? and connection_id = ?`,
data.Options.ProductId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ issueIdGenerator :=
didgen.NewDomainIdGenerator(&models.ZentaoStoryRepoCommit{})
+ convertor, err := api.NewDataConverter(api.DataConverterArgs{
+ InputRowType: reflect.TypeOf(models.ZentaoStoryRepoCommit{}),
+ Input: cursor,
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_STORY_REPO_COMMITS_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
+ toolEntity := inputRow.(*models.ZentaoStoryRepoCommit)
+ domainEntity := &crossdomain.IssueRepoCommit{
+ IssueId:
issueIdGenerator.Generate(data.Options.ConnectionId, toolEntity.IssueId),
+ RepoUrl: toolEntity.RepoUrl,
+ CommitSha: toolEntity.CommitSha,
+ }
+ host, namespace, repoName, err :=
parseRepoUrl(domainEntity.RepoUrl)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+ domainEntity.Host = host
+ domainEntity.Namespace = namespace
+ domainEntity.RepoName = repoName
+
+ var results []interface{}
+ results = append(results, domainEntity)
+ return results, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return convertor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/story_repo_commits_extractor.go
b/backend/plugins/zentao/tasks/story_repo_commits_extractor.go
new file mode 100644
index 000000000..84a6fa294
--- /dev/null
+++ b/backend/plugins/zentao/tasks/story_repo_commits_extractor.go
@@ -0,0 +1,91 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "regexp"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractStoryRepoCommits
+
+var ExtractStoryRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "extractStoryRepoCommits",
+ EntryPoint: ExtractStoryRepoCommits,
+ EnabledByDefault: true,
+ Description: "extract Zentao story repo commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractStoryRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+
+ // this Extract only work for product
+ if data.Options.ProductId == 0 {
+ return nil
+ }
+
+ re := regexp.MustCompile(`(\d+)(?:,\s*(\d+))*`)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_STORY_REPO_COMMITS_TABLE,
+ },
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ res := &models.ZentaoStoryRepoCommitsRes{}
+ err := json.Unmarshal(row.Data, res)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ results := make([]interface{}, 0)
+ match := re.FindStringSubmatch(res.Log.Comment)
+ for i := 1; i < len(match); i++ {
+ if match[i] != "" {
+ storyRepoCommits :=
&models.ZentaoStoryRepoCommit{
+ ConnectionId:
data.Options.ConnectionId,
+ Product:
data.Options.ProductId,
+ Project:
data.Options.ProjectId,
+ RepoUrl: res.Repo.CodePath,
+ CommitSha: res.Revision,
+ IssueId: match[i], //
story id
+ }
+ results = append(results,
storyRepoCommits)
+ }
+ }
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/bug_commits_collector.go
b/backend/plugins/zentao/tasks/task_commits_collector.go
similarity index 66%
copy from backend/plugins/zentao/tasks/bug_commits_collector.go
copy to backend/plugins/zentao/tasks/task_commits_collector.go
index 639885f29..928921cb0 100644
--- a/backend/plugins/zentao/tasks/bug_commits_collector.go
+++ b/backend/plugins/zentao/tasks/task_commits_collector.go
@@ -19,10 +19,7 @@ package tasks
import (
"encoding/json"
- "fmt"
- "io"
"net/http"
- "net/url"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -32,42 +29,48 @@ import (
"github.com/apache/incubator-devlake/plugins/zentao/models"
)
-const RAW_BUG_COMMITS_TABLE = "zentao_api_bug_commits"
+const RAW_TASK_COMMITS_TABLE = "zentao_api_task_commits"
-var _ plugin.SubTaskEntryPoint = CollectBugCommits
+var _ plugin.SubTaskEntryPoint = CollectTaskCommits
-var CollectBugCommitsMeta = plugin.SubTaskMeta{
- Name: "collectBugCommits",
- EntryPoint: CollectBugCommits,
+var CollectTaskCommitsMeta = plugin.SubTaskMeta{
+ Name: "collectTaskCommits",
+ EntryPoint: CollectTaskCommits,
EnabledByDefault: true,
- Description: "Collect Bug Commits data from Zentao api",
+ Description: "Collect Task Commits data from Zentao api",
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
}
-func CollectBugCommits(taskCtx plugin.SubTaskContext) errors.Error {
+func CollectTaskCommits(taskCtx plugin.SubTaskContext) errors.Error {
db := taskCtx.GetDal()
data := taskCtx.GetData().(*ZentaoTaskData)
+ // this collect only work for project
+ if data.Options.ProjectId == 0 {
+ return nil
+ }
+
// state manager
collectorWithState, err :=
api.NewStatefulApiCollector(api.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_TASK_COMMITS_TABLE,
}, data.TimeAfter)
if err != nil {
return err
}
- // load bugs id from db
+ // load stories id from db
clauses := []dal.Clause{
- dal.Select("id"),
- dal.From(&models.ZentaoBug{}),
+ dal.Select("id, last_edited_date"),
+ dal.From(&models.ZentaoTask{}),
dal.Where(
- "product = ? AND connection_id = ?",
- data.Options.ProductId, data.Options.ConnectionId,
+ "project = ? AND connection_id = ?",
+ data.Options.ProjectId, data.Options.ConnectionId,
),
}
// incremental collection
@@ -75,47 +78,47 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
if incremental {
clauses = append(
clauses,
- dal.Where("updated_at > ?",
collectorWithState.LatestState.LatestSuccessStart),
+ dal.Where("last_edited_date is not null and
last_edited_date > ?", collectorWithState.LatestState.LatestSuccessStart),
)
}
cursor, err := db.Cursor(clauses...)
if err != nil {
return err
}
- iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoBug{}))
+
+ iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoTask{}))
if err != nil {
return err
}
- // collect bug commits
+
+ // collect task commits
err = collectorWithState.InitCollector(api.ApiCollectorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_TASK_COMMITS_TABLE,
},
ApiClient: data.ApiClient,
PageSize: 100,
Input: iterator,
Incremental: incremental,
- UrlTemplate: "bugs/{{ .Input.ID }}",
- Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("per_page", fmt.Sprintf("%v",
reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
+ UrlTemplate: "tasks/{{ .Input.ID }}",
ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- body, err := io.ReadAll(res.Body)
+ var data struct {
+ Actions []json.RawMessage `json:"actions"`
+ }
+ err := api.UnmarshalResponse(res, &data)
if err != nil {
- return nil, errors.Convert(err)
+ return nil, err
}
- res.Body.Close()
- return []json.RawMessage{body}, nil
+ return data.Actions, nil
+
},
+ AfterResponse: ignoreHTTPStatus404,
})
if err != nil {
return err
@@ -124,6 +127,6 @@ func CollectBugCommits(taskCtx plugin.SubTaskContext)
errors.Error {
return collectorWithState.Execute()
}
-type SimpleZentaoBug struct {
+type SimpleZentaoTask struct {
ID int64 `json:"id"`
}
diff --git a/backend/plugins/zentao/tasks/task_commits_extractor.go
b/backend/plugins/zentao/tasks/task_commits_extractor.go
new file mode 100644
index 000000000..1ccef48da
--- /dev/null
+++ b/backend/plugins/zentao/tasks/task_commits_extractor.go
@@ -0,0 +1,115 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "net/url"
+ "regexp"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskCommits
+
+var ExtractTaskCommitsMeta = plugin.SubTaskMeta{
+ Name: "extractTaskCommits",
+ EntryPoint: ExtractTaskCommits,
+ EnabledByDefault: true,
+ Description: "extract Zentao task commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+
+ // this Extract only work for project
+ if data.Options.ProjectId == 0 {
+ return nil
+ }
+
+ re := regexp.MustCompile(`href='(.*?)'`)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_TASK_COMMITS_TABLE,
+ },
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ res := &models.ZentaoTaskCommitsRes{}
+ err := json.Unmarshal(row.Data, res)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ // only linked2revision action is valid
+ if res.Action != "linked2revision" {
+ return nil, nil
+ }
+
+ taskCommits := &models.ZentaoTaskCommit{
+ ConnectionId: data.Options.ConnectionId,
+ ID: res.ID,
+ ObjectType: res.ObjectType,
+ ObjectID: res.ObjectID,
+ Product: data.Options.ProductId,
+ Project: data.Options.ProjectId,
+ Execution: res.Execution,
+ Actor: res.Actor,
+ Action: res.Action,
+ Date: res.Date,
+ Comment: res.Comment,
+ ActionRead: res.Read,
+ Vision: res.Vision,
+ Efforted: res.Efforted,
+ ActionDesc: res.Desc,
+ }
+
+ match := re.FindStringSubmatch(res.Extra)
+ if len(match) > 1 {
+ taskCommits.Extra = match[1]
+ } else {
+ return nil, nil
+ }
+ u, err := url.Parse(match[1])
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ taskCommits.Host = u.Host
+ taskCommits.RepoRevision = u.Path
+
+ results := make([]interface{}, 0)
+ results = append(results, taskCommits)
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/bug_commits_collector.go
b/backend/plugins/zentao/tasks/task_repo_commits_collector.go
similarity index 50%
copy from backend/plugins/zentao/tasks/bug_commits_collector.go
copy to backend/plugins/zentao/tasks/task_repo_commits_collector.go
index 639885f29..b5fd3f5aa 100644
--- a/backend/plugins/zentao/tasks/bug_commits_collector.go
+++ b/backend/plugins/zentao/tasks/task_repo_commits_collector.go
@@ -19,10 +19,7 @@ package tasks
import (
"encoding/json"
- "fmt"
- "io"
"net/http"
- "net/url"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -32,98 +29,78 @@ import (
"github.com/apache/incubator-devlake/plugins/zentao/models"
)
-const RAW_BUG_COMMITS_TABLE = "zentao_api_bug_commits"
+const RAW_TASK_REPO_COMMITS_TABLE = "zentao_api_task_repo_commits"
-var _ plugin.SubTaskEntryPoint = CollectBugCommits
+var _ plugin.SubTaskEntryPoint = CollectTaskRepoCommits
-var CollectBugCommitsMeta = plugin.SubTaskMeta{
- Name: "collectBugCommits",
- EntryPoint: CollectBugCommits,
+var CollectTaskRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "collectTaskRepoCommits",
+ EntryPoint: CollectTaskRepoCommits,
EnabledByDefault: true,
- Description: "Collect Bug Commits data from Zentao api",
+ Description: "Collect Task Repo Commits data from Zentao api",
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
}
-func CollectBugCommits(taskCtx plugin.SubTaskContext) errors.Error {
+func CollectTaskRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
db := taskCtx.GetDal()
data := taskCtx.GetData().(*ZentaoTaskData)
- // state manager
- collectorWithState, err :=
api.NewStatefulApiCollector(api.RawDataSubTaskArgs{
- Ctx: taskCtx,
- Params: ZentaoApiParams{
- ConnectionId: data.Options.ConnectionId,
- ProductId: data.Options.ProductId,
- },
- Table: RAW_BUG_COMMITS_TABLE,
- }, data.TimeAfter)
- if err != nil {
- return err
- }
-
- // load bugs id from db
+ // load tasks id from db
clauses := []dal.Clause{
- dal.Select("id"),
- dal.From(&models.ZentaoBug{}),
+ dal.Select("object_id, repo_revision"),
+ dal.From(&models.ZentaoTaskCommit{}),
dal.Where(
- "product = ? AND connection_id = ?",
- data.Options.ProductId, data.Options.ConnectionId,
+ "project = ? AND connection_id = ?",
+ data.Options.ProjectId, data.Options.ConnectionId,
),
}
- // incremental collection
- incremental := collectorWithState.IsIncremental()
- if incremental {
- clauses = append(
- clauses,
- dal.Where("updated_at > ?",
collectorWithState.LatestState.LatestSuccessStart),
- )
- }
+
cursor, err := db.Cursor(clauses...)
if err != nil {
return err
}
- iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoBug{}))
+
+ iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(SimpleZentaoTaskCommit{}))
if err != nil {
return err
}
- // collect bug commits
- err = collectorWithState.InitCollector(api.ApiCollectorArgs{
+
+ // collect task repo commits
+ collector, err := api.NewApiCollector(api.ApiCollectorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
Params: ZentaoApiParams{
ConnectionId: data.Options.ConnectionId,
ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
},
- Table: RAW_BUG_COMMITS_TABLE,
+ Table: RAW_TASK_REPO_COMMITS_TABLE,
},
ApiClient: data.ApiClient,
- PageSize: 100,
Input: iterator,
- Incremental: incremental,
- UrlTemplate: "bugs/{{ .Input.ID }}",
- Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("per_page", fmt.Sprintf("%v",
reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
+ UrlTemplate: "../..{{ .Input.RepoRevision }}",
ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- body, err := io.ReadAll(res.Body)
+ var result RepoRevisionResponse
+ err := api.UnmarshalResponse(res, &result)
if err != nil {
- return nil, errors.Convert(err)
+ return nil, err
}
- res.Body.Close()
- return []json.RawMessage{body}, nil
+ byteData := []byte(result.Data)
+ return []json.RawMessage{byteData}, nil
+
},
+ AfterResponse: ignoreHTTPStatus404,
})
if err != nil {
return err
}
- return collectorWithState.Execute()
+ return collector.Execute()
}
-type SimpleZentaoBug struct {
- ID int64 `json:"id"`
+type SimpleZentaoTaskCommit struct {
+ ObjectID int `json:"objectID"`
+ Host string `json:"host"` //the host part of extra
+ RepoRevision string `json:"repoRevision"` // the repoRevisionJson part
of extra
+
}
diff --git a/backend/plugins/zentao/tasks/task_repo_commits_convertor.go
b/backend/plugins/zentao/tasks/task_repo_commits_convertor.go
new file mode 100644
index 000000000..d2eb7cf18
--- /dev/null
+++ b/backend/plugins/zentao/tasks/task_repo_commits_convertor.go
@@ -0,0 +1,93 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ConvertTaskRepoCommits
+
+var ConvertTaskRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "convertTaskRepoCommits",
+ EntryPoint: ConvertTaskRepoCommits,
+ EnabledByDefault: true,
+ Description: "convert Zentao task repo commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertTaskRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+ db := taskCtx.GetDal()
+
+ cursor, err := db.Cursor(
+ dal.From(&models.ZentaoTaskRepoCommit{}),
+ dal.Where(`project = ? and connection_id = ?`,
data.Options.ProjectId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ issueIdGenerator :=
didgen.NewDomainIdGenerator(&models.ZentaoTaskRepoCommit{})
+ convertor, err := api.NewDataConverter(api.DataConverterArgs{
+ InputRowType: reflect.TypeOf(models.ZentaoTaskRepoCommit{}),
+ Input: cursor,
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_TASK_REPO_COMMITS_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
+ toolEntity := inputRow.(*models.ZentaoTaskRepoCommit)
+ domainEntity := &crossdomain.IssueRepoCommit{
+ IssueId:
issueIdGenerator.Generate(data.Options.ConnectionId, toolEntity.IssueId),
+ RepoUrl: toolEntity.RepoUrl,
+ CommitSha: toolEntity.CommitSha,
+ }
+ host, namespace, repoName, err :=
parseRepoUrl(domainEntity.RepoUrl)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+ domainEntity.Host = host
+ domainEntity.Namespace = namespace
+ domainEntity.RepoName = repoName
+
+ var results []interface{}
+ results = append(results, domainEntity)
+ return results, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return convertor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/task_repo_commits_extractor.go
b/backend/plugins/zentao/tasks/task_repo_commits_extractor.go
new file mode 100644
index 000000000..072872331
--- /dev/null
+++ b/backend/plugins/zentao/tasks/task_repo_commits_extractor.go
@@ -0,0 +1,91 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "regexp"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractTaskRepoCommits
+
+var ExtractTaskRepoCommitsMeta = plugin.SubTaskMeta{
+ Name: "extractTaskRepoCommits",
+ EntryPoint: ExtractTaskRepoCommits,
+ EnabledByDefault: true,
+ Description: "extract Zentao task repo commits",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractTaskRepoCommits(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+
+ // this Extract only work for project
+ if data.Options.ProjectId == 0 {
+ return nil
+ }
+
+ re := regexp.MustCompile(`(\d+)(?:,\s*(\d+))*`)
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_TASK_REPO_COMMITS_TABLE,
+ },
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ res := &models.ZentaoTaskRepoCommitsRes{}
+ err := json.Unmarshal(row.Data, res)
+ if err != nil {
+ return nil, errors.Default.WrapRaw(err)
+ }
+
+ results := make([]interface{}, 0)
+ match := re.FindStringSubmatch(res.Log.Comment)
+ for i := 1; i < len(match); i++ {
+ if match[i] != "" {
+ taskRepoCommits :=
&models.ZentaoTaskRepoCommit{
+ ConnectionId:
data.Options.ConnectionId,
+ Product:
data.Options.ProductId,
+ Project:
data.Options.ProjectId,
+ RepoUrl: res.Repo.CodePath,
+ CommitSha: res.Revision,
+ IssueId: match[i], // task
id
+ }
+ results = append(results,
taskRepoCommits)
+ }
+ }
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}