This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 135802c2c Zentao update the data transformation logic (#5323)
135802c2c is described below
commit 135802c2c6e70361aae53c121796ee2789695bbf
Author: mappjzc <[email protected]>
AuthorDate: Thu Jun 1 17:03:51 2023 +0800
Zentao update the data transformation logic (#5323)
* fix: data transformation logic
Update the data transformation logic
Fix issue sprints and boards.
Add changelog convert.
Nddtfjiang <[email protected]>
* feat: add translation rule
Add translation rule.
Nddtfjiang <[email protected]>
* fix: e2e fix
Fix e2e test error
Fix issue GetStatus lost
Nddtfjiang <[email protected]>
* feat: add typemapping to zentao
Add TypeMapping to zentao.
Fix some swager bugs.
Fix zentao e2e for merge.
Change translation rule to scope config.
Nddtfjiang <[email protected]>
---
backend/core/models/domainlayer/ticket/issue.go | 34 ++++++
.../core/models/migrationscripts/archived/base.go | 8 +-
.../pluginhelper/api/scope_generic_helper.go | 13 +-
backend/plugins/pagerduty/api/swagger.go | 2 +-
backend/plugins/trello/api/scope.go | 8 +-
backend/plugins/zentao/api/init.go | 13 +-
backend/plugins/zentao/api/scope.go | 4 +-
backend/plugins/zentao/api/scope_config.go | 83 +++++++++++++
backend/plugins/zentao/e2e/changelog_test.go | 10 ++
.../e2e/snapshot_tables/_tool_zentao_bugs.csv | 10 +-
.../_tool_zentao_changelog_detail.csv | 56 ++++-----
.../e2e/snapshot_tables/_tool_zentao_stories.csv | 16 +--
.../e2e/snapshot_tables/_tool_zentao_tasks.csv | 8 +-
.../zentao/e2e/snapshot_tables/boards_product.csv | 2 +-
.../e2e/snapshot_tables/execution_sprint.csv | 4 +-
.../e2e/snapshot_tables/issue_changelogs.csv | 19 +++
.../zentao/e2e/snapshot_tables/issues_bug.csv | 8 +-
.../zentao/e2e/snapshot_tables/issues_story.csv | 14 +--
.../zentao/e2e/snapshot_tables/issues_task.csv | 6 +-
backend/plugins/zentao/impl/impl.go | 21 ++++
.../plugins/zentao/models/archived/scope_config.go | 38 ++++++
backend/plugins/zentao/models/bug.go | 3 +
.../20230519_add_init_changelog_tables.go | 2 +-
.../migrationscripts/20230601_add_scope_config.go | 76 ++++++++++++
.../zentao/models/migrationscripts/register.go | 1 +
backend/plugins/zentao/models/remote_db.go | 2 +-
backend/plugins/zentao/models/scope_config.go | 38 ++++++
backend/plugins/zentao/models/story.go | 3 +
backend/plugins/zentao/models/task.go | 3 +
backend/plugins/zentao/tasks/bug_convertor.go | 40 +++----
backend/plugins/zentao/tasks/bug_extractor.go | 20 ++++
.../plugins/zentao/tasks/changelog_convertor.go | 131 +++++++++++++++++++++
.../plugins/zentao/tasks/execution_convertor.go | 7 +-
backend/plugins/zentao/tasks/product_convertor.go | 4 +
backend/plugins/zentao/tasks/project_convertor.go | 4 +
backend/plugins/zentao/tasks/shared.go | 94 ++++++++++++++-
backend/plugins/zentao/tasks/story_convertor.go | 47 ++++----
backend/plugins/zentao/tasks/story_extractor.go | 21 ++++
backend/plugins/zentao/tasks/task_convertor.go | 47 ++++----
backend/plugins/zentao/tasks/task_data.go | 68 +++++++++--
backend/plugins/zentao/tasks/task_extractor.go | 21 ++++
41 files changed, 847 insertions(+), 162 deletions(-)
diff --git a/backend/core/models/domainlayer/ticket/issue.go
b/backend/core/models/domainlayer/ticket/issue.go
index 71852b738..977ea69ed 100644
--- a/backend/core/models/domainlayer/ticket/issue.go
+++ b/backend/core/models/domainlayer/ticket/issue.go
@@ -64,8 +64,42 @@ const (
INCIDENT = "INCIDENT"
TASK = "TASK"
+ // status
TODO = "TODO"
DONE = "DONE"
IN_PROGRESS = "IN_PROGRESS"
OTHER = "OTHER"
)
+
+type StatusRule struct {
+ InProgress []string
+ Todo []string
+ Done []string
+ Other []string
+ Default string
+}
+
+// GetStatus compare the input with rule for return the enmu value of status
+func GetStatus(rule *StatusRule, input interface{}) string {
+ for _, inp := range rule.InProgress {
+ if inp == input {
+ return IN_PROGRESS
+ }
+ }
+ for _, Todo := range rule.Todo {
+ if Todo == input {
+ return TODO
+ }
+ }
+ for _, done := range rule.Done {
+ if done == input {
+ return DONE
+ }
+ }
+ for _, Other := range rule.Other {
+ if Other == input {
+ return OTHER
+ }
+ }
+ return rule.Default
+}
diff --git a/backend/core/models/migrationscripts/archived/base.go
b/backend/core/models/migrationscripts/archived/base.go
index 2d37aa076..2e30cf1d1 100644
--- a/backend/core/models/migrationscripts/archived/base.go
+++ b/backend/core/models/migrationscripts/archived/base.go
@@ -18,8 +18,9 @@ limitations under the License.
package archived
import (
- "golang.org/x/exp/constraints"
"time"
+
+ "golang.org/x/exp/constraints"
)
type DomainEntity struct {
@@ -33,6 +34,11 @@ type Model struct {
UpdatedAt time.Time `json:"updatedAt"`
}
+type ScopeConfig struct {
+ Model
+ Entities []string `gorm:"type:json;serializer:json" json:"entities"
mapstructure:"entities"`
+}
+
type GenericModel[T string | constraints.Unsigned] struct {
ID T `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"createdAt"`
diff --git a/backend/helpers/pluginhelper/api/scope_generic_helper.go
b/backend/helpers/pluginhelper/api/scope_generic_helper.go
index 6d094fca8..78d161ec3 100644
--- a/backend/helpers/pluginhelper/api/scope_generic_helper.go
+++ b/backend/helpers/pluginhelper/api/scope_generic_helper.go
@@ -20,6 +20,12 @@ package api
import (
"encoding/json"
"fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
"github.com/apache/incubator-devlake/core/context"
"github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
@@ -30,11 +36,6 @@ import (
serviceHelper
"github.com/apache/incubator-devlake/helpers/pluginhelper/services"
"github.com/go-playground/validator/v10"
"github.com/mitchellh/mapstructure"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "time"
)
var (
@@ -42,7 +43,7 @@ var (
tablesCacheLoader = new(sync.Once)
)
-type NoTransformation struct{}
+type NoScopeConfig struct{}
type (
GenericScopeApiHelper[Conn any, Scope any, Tr any] struct {
diff --git a/backend/plugins/pagerduty/api/swagger.go
b/backend/plugins/pagerduty/api/swagger.go
index 3d8f2a554..ffd6a9d68 100644
--- a/backend/plugins/pagerduty/api/swagger.go
+++ b/backend/plugins/pagerduty/api/swagger.go
@@ -21,7 +21,7 @@ import (
"github.com/apache/incubator-devlake/plugins/pagerduty/tasks"
)
-type PagerdutyTaskOptions tasks.PagerDutyOptions
+type PagerDutyTaskOptions tasks.PagerDutyOptions
// @Summary pagerduty task options for pipelines
// @Description This is a dummy API to demonstrate the available task options
for pagerduty pipelines
diff --git a/backend/plugins/trello/api/scope.go
b/backend/plugins/trello/api/scope.go
index 45db6372c..56c399197 100644
--- a/backend/plugins/trello/api/scope.go
+++ b/backend/plugins/trello/api/scope.go
@@ -20,15 +20,19 @@ package api
import (
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/trello/models"
)
+type ScopeReq api.ScopeReq[models.TrelloBoard]
+
// PutScope create or update trello board
// @Summary create or update trello board
// @Description Create or update trello board
// @Tags plugins/trello
// @Accept application/json
// @Param connectionId path int false "connection ID"
-// @Param scope body req true "json"
+// @Param scope body ScopeReq true "json"
// @Success 200 {object} []models.TrelloBoard
// @Failure 400 {object} shared.ApiBody "Bad Request"
// @Failure 500 {object} shared.ApiBody "Internal Error"
@@ -60,7 +64,7 @@ func UpdateScope(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, err
// @Param connectionId path int false "connection ID"
// @Param pageSize query int false "page size, default 50"
// @Param page query int false "page size, default 1"
-// @Success 200 {object} []apiBoard
+// @Success 200 {object} []models.TrelloBoard
// @Failure 400 {object} shared.ApiBody "Bad Request"
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/trello/connections/{connectionId}/scopes/ [GET]
diff --git a/backend/plugins/zentao/api/init.go
b/backend/plugins/zentao/api/init.go
index 440ea3e56..6eca63dab 100644
--- a/backend/plugins/zentao/api/init.go
+++ b/backend/plugins/zentao/api/init.go
@@ -31,12 +31,13 @@ type MixScopes struct {
var vld *validator.Validate
var connectionHelper *api.ConnectionApiHelper
-var productScopeHelper *api.ScopeApiHelper[models.ZentaoConnection,
models.ZentaoProduct, api.NoTransformation]
-var projectScopeHelper *api.ScopeApiHelper[models.ZentaoConnection,
models.ZentaoProject, api.NoTransformation]
+var productScopeHelper *api.ScopeApiHelper[models.ZentaoConnection,
models.ZentaoProduct, models.ZentaoScopeConfig]
+var projectScopeHelper *api.ScopeApiHelper[models.ZentaoConnection,
models.ZentaoProject, models.ZentaoScopeConfig]
var productRemoteHelper *api.RemoteApiHelper[models.ZentaoConnection,
models.ZentaoProduct, models.ZentaoProductRes, api.BaseRemoteGroupResponse]
var projectRemoteHelper *api.RemoteApiHelper[models.ZentaoConnection,
models.ZentaoProject, models.ZentaoProject, api.NoRemoteGroupResponse]
var basicRes context.BasicRes
+var scHelper *api.ScopeConfigHelper[models.ZentaoScopeConfig]
func Init(br context.BasicRes) {
basicRes = br
@@ -49,11 +50,11 @@ func Init(br context.BasicRes) {
ScopeIdFieldName: "Id",
ScopeIdColumnName: "id",
}
- productScopeHelper = api.NewScopeHelper[models.ZentaoConnection,
models.ZentaoProduct, api.NoTransformation](
+ productScopeHelper = api.NewScopeHelper[models.ZentaoConnection,
models.ZentaoProduct, models.ZentaoScopeConfig](
basicRes,
vld,
connectionHelper,
- api.NewScopeDatabaseHelperImpl[models.ZentaoConnection,
models.ZentaoProduct, api.NoTransformation](
+ api.NewScopeDatabaseHelperImpl[models.ZentaoConnection,
models.ZentaoProduct, models.ZentaoScopeConfig](
basicRes, connectionHelper, productParams),
productParams,
nil,
@@ -62,11 +63,11 @@ func Init(br context.BasicRes) {
ScopeIdFieldName: "Project",
ScopeIdColumnName: "project",
}
- projectScopeHelper = api.NewScopeHelper[models.ZentaoConnection,
models.ZentaoProject, api.NoTransformation](
+ projectScopeHelper = api.NewScopeHelper[models.ZentaoConnection,
models.ZentaoProject, models.ZentaoScopeConfig](
basicRes,
vld,
connectionHelper,
- api.NewScopeDatabaseHelperImpl[models.ZentaoConnection,
models.ZentaoProject, api.NoTransformation](
+ api.NewScopeDatabaseHelperImpl[models.ZentaoConnection,
models.ZentaoProject, models.ZentaoScopeConfig](
basicRes, connectionHelper, projectParams),
projectParams,
nil,
diff --git a/backend/plugins/zentao/api/scope.go
b/backend/plugins/zentao/api/scope.go
index f2a03019b..980260733 100644
--- a/backend/plugins/zentao/api/scope.go
+++ b/backend/plugins/zentao/api/scope.go
@@ -26,14 +26,14 @@ import (
type ProductScopeRes struct {
models.ZentaoProduct
- TransformationRuleName string `json:"transformationRuleName,omitempty"`
+ ScopeConfigName string `json:"scopeConfigName,omitempty"`
}
type ProductScopeReq api.ScopeReq[models.ZentaoProduct]
type ProjectScopeRes struct {
models.ZentaoProject
- TransformationRuleName string `json:"transformationRuleName,omitempty"`
+ ScopeConfigName string `json:"scopeConfigName,omitempty"`
}
type ProjectScopeReq api.ScopeReq[models.ZentaoProject]
diff --git a/backend/plugins/zentao/api/scope_config.go
b/backend/plugins/zentao/api/scope_config.go
new file mode 100644
index 000000000..b12ed6fc1
--- /dev/null
+++ b/backend/plugins/zentao/api/scope_config.go
@@ -0,0 +1,83 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+)
+
+// CreateScopeConfig create scope config for Zentao
+// @Summary create scope config for Zentao
+// @Description create scope config for Zentao
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int true "connectionId"
+// @Param scopeConfig body models.ZentaoScopeConfig true "scope config"
+// @Success 200 {object} models.ZentaoScopeConfig
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs [POST]
+func CreateScopeConfig(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ return scHelper.Create(input)
+}
+
+// UpdateScopeConfig update scope config for Zentao
+// @Summary update scope config for Zentao
+// @Description update scope config for Zentao
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param id path int true "id"
+// @Param connectionId path int true "connectionId"
+// @Param scopeConfig body models.ZentaoScopeConfig true "scope config"
+// @Success 200 {object} models.ZentaoScopeConfig
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs/{id}
[PATCH]
+func UpdateScopeConfig(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ return scHelper.Update(input)
+}
+
+// GetScopeConfig return one scope config
+// @Summary return one scope config
+// @Description return one scope config
+// @Tags plugins/zentao
+// @Param id path int true "id"
+// @Param connectionId path int true "connectionId"
+// @Success 200 {object} models.ZentaoScopeConfig
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs/{id} [GET]
+func GetScopeConfig(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ return scHelper.Get(input)
+}
+
+// GetScopeConfigList return all scope configs
+// @Summary return all scope configs
+// @Description return all scope configs
+// @Tags plugins/zentao
+// @Param connectionId path int true "connectionId"
+// @Param pageSize query int false "page size, default 50"
+// @Param page query int false "page size, default 1"
+// @Success 200 {object} []models.ZentaoScopeConfig
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs [GET]
+func GetScopeConfigList(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ return scHelper.List(input)
+}
diff --git a/backend/plugins/zentao/e2e/changelog_test.go
b/backend/plugins/zentao/e2e/changelog_test.go
index aed72b615..0061d5507 100644
--- a/backend/plugins/zentao/e2e/changelog_test.go
+++ b/backend/plugins/zentao/e2e/changelog_test.go
@@ -22,6 +22,7 @@ import (
"github.com/apache/incubator-devlake/core/config"
"github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
"github.com/apache/incubator-devlake/core/runner"
"github.com/apache/incubator-devlake/helpers/e2ehelper"
"github.com/apache/incubator-devlake/impls/dalgorm"
@@ -92,4 +93,13 @@ func TestZentaoDbGetDataFlow(t *testing.T) {
CSVRelPath:
"./snapshot_tables/_tool_zentao_changelog_detail.csv",
IgnoreTypes: []interface{}{common.NoPKModel{}},
})
+
+ dataflowTester.FlushTabler(&ticket.IssueChangelogs{})
+ dataflowTester.Subtask(tasks.ConvertChangelogMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(
+ &ticket.IssueChangelogs{},
+ e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/issue_changelogs.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
}
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
index 1f49d1a4c..5a5dd4498 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
@@ -1,13 +1,13 @@
-connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,fee
[...]
+connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,fee
[...]
1,1,7,3,0,0,0,8,1,0,1,1,1,0,0,首页页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入首页</p>
<p>[结果]出现乱码 </p>
-<p>[期望]正常显示</p>",active,,,0,0,,,,7,测试甲,2012-06-05T02:56:11.000+00:00,主干,4,开发甲,2012-06-05T02:56:11.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal
+<p>[期望]正常显示</p>",active,,,0,0,,,,7,测试甲,2012-06-05T02:56:11.000+00:00,主干,4,开发甲,2012-06-05T02:56:11.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,IN_PROGRESS,BUG,
1,2,7,3,0,0,0,9,1,1,2,1,15,0,0,新闻中心页面问题,hh,3,2,codeerror,",windows",",chrome",,,"<p>[步骤]进入新闻中心</p>
<p>[结果]页面出现乱码</p>
-<p>[期望]正常显示rew</p>",delay,,,1,1,2022-10-05T04:16:44.000+00:00,,[email protected],7,测试甲,2012-06-05T02:57:11.000+00:00,主干,0,,2022-10-05T04:19:22.000+00:00,2022-10-06,0,,,,0,,0,,0,0,0,0,,,,,,,1,1,2022-10-05T04:19:22.000+00:00,0,2,3,0,过期Bug,normal
+<p>[期望]正常显示rew</p>",delay,,,1,1,2022-10-05T04:16:44.000+00:00,,[email protected],7,测试甲,2012-06-05T02:57:11.000+00:00,主干,0,,2022-10-05T04:19:22.000+00:00,2022-10-06,0,,,,0,,0,,0,0,0,0,,,,,,,1,1,2022-10-05T04:19:22.000+00:00,0,2,3,0,过期Bug,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,IN_PROGRESS,BUG,
1,3,7,3,0,0,0,10,1,0,3,2,6,0,0,成果展示页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入成果展示 </p>
<p>[结果]乱码</p>
-<p>[期望]正常显示</p>",active,,,0,0,,,,8,测试乙,2012-06-05T02:58:22.000+00:00,主干,4,开发甲,2012-06-05T02:58:22.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal
+<p>[期望]正常显示</p>",active,,,0,0,,,,8,测试乙,2012-06-05T02:58:22.000+00:00,主干,4,开发甲,2012-06-05T02:58:22.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,IN_PROGRESS,BUG,
1,4,7,3,0,0,0,11,1,0,4,1,9,0,0,售后服务页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入售后服务</p>
<p>[结果]乱码</p>
-<p>[期望]正常显示</p>",resolved,,,1,0,,,,9,测试丙,2012-06-05T03:00:19.000+00:00,主干,9,测试丙,2022-10-05T04:10:08.000+00:00,,1,fixed,主干,2022-10-05T04:09:59.000+00:00,0,,0,,0,0,0,0,,,,,,,0,1,2022-10-05T04:10:08.000+00:00,0,1,3,0,已解决,normal
+<p>[期望]正常显示</p>",resolved,,,1,0,,,,9,测试丙,2012-06-05T03:00:19.000+00:00,主干,9,测试丙,2022-10-05T04:10:08.000+00:00,,1,fixed,主干,2022-10-05T04:09:59.000+00:00,0,,0,,0,0,0,0,,,,,,,0,1,2022-10-05T04:10:08.000+00:00,0,1,3,0,已解决,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,DONE,BUG,
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
index 88aee2bc1..ab5020347 100644
---
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
+++
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
@@ -1,29 +1,29 @@
connection_id,id,changelog_id,field,old,new,diff
-1,68,68,begin,2012-06-05,2020-06-05,
-1,69,69,end,2012-12-04,2021-12-04,
-1,70,70,days,184,391,
-1,71,71,status,done,wait,
-1,72,72,begin,2013-06-05,2021-06-05,
-1,73,73,end,2014-06-04,2022-06-04,
-1,74,74,days,365,260,
-1,75,75,type,interface,codeerror,
-1,76,76,pri,0,1,
-1,77,77,pri,0,2,
-1,78,78,pri,0,1,
-1,79,79,pri,0,1,
-1,80,80,build,trunk,1,
-1,81,81,begin,2012-06-05,2020-06-05,
-1,82,82,end,2013-06-21,2021-06-21,
-1,83,83,begin,2000-01-01,2020-06-05,
-1,84,84,end,2015-01-01,2021-06-04,
-1,85,85,status,wait,doing,
-1,91,91,consumed,0,8,
-1,92,92,realStarted,0000-00-00 00:00:00,2021-04-28 13:17:54,
-1,93,93,finishedDate,,2021-04-01 16:00,
-1,94,94,status,wait,done,
-1,95,95,left,7,0,
-1,96,96,finishedBy,,dev1,
-1,97,97,realStarted,0000-00-00 00:00:00,2021-04-01 16:00,
-1,98,98,status,wait,doing,
-1,99,99,realStarted,0000-00-00 00:00:00,2021-04-01 08:00,
-1,100,100,status,wait,doing,
+1,68,112,begin,2012-06-05,2020-06-05,
+1,69,112,end,2012-12-04,2021-12-04,
+1,70,112,days,184,391,
+1,71,112,status,done,wait,
+1,72,113,begin,2013-06-05,2021-06-05,
+1,73,113,end,2014-06-04,2022-06-04,
+1,74,113,days,365,260,
+1,75,114,type,interface,codeerror,
+1,76,114,pri,0,1,
+1,77,115,pri,0,2,
+1,78,116,pri,0,1,
+1,79,117,pri,0,1,
+1,80,118,build,trunk,1,
+1,81,118,begin,2012-06-05,2020-06-05,
+1,82,118,end,2013-06-21,2021-06-21,
+1,83,119,begin,2000-01-01,2020-06-05,
+1,84,119,end,2015-01-01,2021-06-04,
+1,85,120,status,wait,doing,
+1,91,165,consumed,0,8,
+1,92,165,realStarted,0000-00-00 00:00:00,2021-04-28 13:17:54,
+1,93,165,finishedDate,,2021-04-01 16:00,
+1,94,165,status,wait,done,
+1,95,165,left,7,0,
+1,96,165,finishedBy,,dev1,
+1,97,166,realStarted,0000-00-00 00:00:00,2021-04-01 16:00,
+1,98,166,status,wait,doing,
+1,99,169,realStarted,0000-00-00 00:00:00,2021-04-01 08:00,
+1,100,169,status,wait,doing,
diff --git
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
index 2ece4f19a..ccce179fc 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
@@ -1,8 +1,8 @@
-connection_id,id,product,branch,version,order_in,vision,parent,module,plan,source,source_note,from_bug,feedback,title,keywords,type,category,pri,estimate,status,sub_status,color,stage,lib,from_story,from_version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,approved_date,last_edited_id,last_edited_date,changed_date,reviewed_by_id,reviewed_date,closed_id,closed_date,closed_reason,activated_date,to_bug,child_stories,link_stories,link_requirements,dup
[...]
-1,1,3,0,1,0,rnd,0,1,1,po,,0,0,首页设计和开发,,story,feature,1,1,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:09:49.000+00:00,2,产品经理,,,2,2012-06-05T02:25:19.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
-1,2,3,0,1,0,rnd,0,2,1,po,,0,0,新闻中心的设计和开发。,,story,feature,1,1,active,,,projected,0,0,1,2,产品经理,2012-06-05T02:16:37.000+00:00,2,产品经理,2012-06-05T02:16:37.000+00:00,,2,2012-06-05T02:25:33.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
-1,3,3,0,2,0,rnd,0,3,1,po,,0,0,成果展示的设计和开发,,story,feature,1,0,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:18:10.000+00:00,2,产品经理,2012-06-05T02:18:10.000+00:00,,2,2012-06-05T02:25:38.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
-1,4,3,0,1,0,rnd,0,4,1,po,,0,0,售后服务的设计和开发,,story,feature,1,1,active,,,developed,0,0,1,2,产品经理,2012-06-05T02:20:16.000+00:00,2,产品经理,2012-06-05T02:20:16.000+00:00,,2,2012-06-05T02:25:42.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
-1,5,3,0,1,0,rnd,0,5,1,po,,0,0,诚聘英才的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:21:39.000+00:00,2,产品经理,2012-06-05T02:21:39.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
-1,6,3,0,1,0,rnd,0,6,1,po,,0,0,合作洽谈的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:23:11.000+00:00,2,产品经理,2012-06-05T02:23:11.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
-1,7,3,0,1,0,rnd,0,7,1,po,,0,0,关于我们的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:24:19.000+00:00,2,产品经理,2012-06-05T02:24:19.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
+connection_id,id,product,branch,version,order_in,vision,parent,module,plan,source,source_note,from_bug,feedback,title,keywords,type,category,pri,estimate,status,sub_status,color,stage,lib,from_story,from_version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,approved_date,last_edited_id,last_edited_date,changed_date,reviewed_by_id,reviewed_date,closed_id,closed_date,closed_reason,activated_date,to_bug,child_stories,link_stories,link_requirements,dup
[...]
+1,1,3,0,1,0,rnd,0,1,1,po,,0,0,首页设计和开发,,story,feature,1,1,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:09:49.000+00:00,2,产品经理,,,2,2012-06-05T02:25:19.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,2,3,0,1,0,rnd,0,2,1,po,,0,0,新闻中心的设计和开发。,,story,feature,1,1,active,,,projected,0,0,1,2,产品经理,2012-06-05T02:16:37.000+00:00,2,产品经理,2012-06-05T02:16:37.000+00:00,,2,2012-06-05T02:25:33.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,3,3,0,2,0,rnd,0,3,1,po,,0,0,成果展示的设计和开发,,story,feature,1,0,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:18:10.000+00:00,2,产品经理,2012-06-05T02:18:10.000+00:00,,2,2012-06-05T02:25:38.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,4,3,0,1,0,rnd,0,4,1,po,,0,0,售后服务的设计和开发,,story,feature,1,1,active,,,developed,0,0,1,2,产品经理,2012-06-05T02:20:16.000+00:00,2,产品经理,2012-06-05T02:20:16.000+00:00,,2,2012-06-05T02:25:42.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,5,3,0,1,0,rnd,0,5,1,po,,0,0,诚聘英才的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:21:39.000+00:00,2,产品经理,2012-06-05T02:21:39.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,6,3,0,1,0,rnd,0,6,1,po,,0,0,合作洽谈的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:23:11.000+00:00,2,产品经理,2012-06-05T02:23:11.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,7,3,0,1,0,rnd,0,7,1,po,,0,0,关于我们的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:24:19.000+00:00,2,产品经理,2012-06-05T02:24:19.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
index 40337d85f..a5c08897c 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
@@ -1,4 +1,4 @@
-connection_id,id,project,parent,execution,module,design,story,story_version,design_version,from_bug,feedback,from_issue,name,type,mode,pri,estimate,consumed,deadline,status,sub_status,color,description,version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,est_started,real_started,finished_id,finished_date,finished_list,canceled_id,canceled_date,closed_by_id,closed_date,plan_duration,real_duration,closed_reason,last_edited_id,last_edited_date,activa
[...]
-1,1,1,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span>
</span><br /><div><br
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11
-1,2,1,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,12.1,2.1,2022-10-01,wait,,,任务描述<span>
</span><br /><div><br
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3
-1,3,1,-1,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,11.2,0,2022-10-01,wait,,,任务描述<span>
</span><br /><div><br
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121
+connection_id,id,project,parent,execution,module,design,story,story_version,design_version,from_bug,feedback,from_issue,name,type,mode,pri,estimate,consumed,deadline,status,sub_status,color,description,version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,est_started,real_started,finished_id,finished_date,finished_list,canceled_id,canceled_date,closed_by_id,closed_date,plan_duration,real_duration,closed_reason,last_edited_id,last_edited_date,activa
[...]
+1,1,1,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span>
</span><br /><div><br
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,TODO,TASK,
+1,2,1,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,12.1,2.1,2022-10-01,wait,,,任务描述<span>
</span><br /><div><br
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,TODO,TASK,
+1,3,1,-1,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,11.2,0,2022-10-01,wait,,,任务描述<span>
</span><br /><div><br
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,TODO,TASK,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
b/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
index 11a53ea68..0c3d53b5d 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
@@ -1,2 +1,2 @@
id,name,description,url,created_date,type
-zentao:ZentaoProduct:1:3,产品名称1,"<span
style=""background-color:#FFFFFF;"">产品描述1</span>",,2022-11-17T06:42:25.000+00:00,product/normal
+zentao:ZentaoProduct:1:3,产品名称1,"<span
style=""background-color:#FFFFFF;"">产品描述1</span>",/product-index-3.html,2022-11-17T06:42:25.000+00:00,product/normal
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
b/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
index 81e23be75..13a04dcab 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
@@ -1,3 +1,3 @@
id,name,url,status,started_date,ended_date,completed_date,original_board_id
-zentao:ZentaoExecution:1:1,企业网站第一期,",7,1,",ACTIVE,,,,zentao:ZentaoProject:1:1
-zentao:ZentaoExecution:1:12,TR5,",1091,12,",CLOSED,2022-07-07T00:00:00.000+00:00,,,zentao:ZentaoProject:1:12
+zentao:ZentaoExecution:1:1,企业网站第一期,",7,1,",ACTIVE,,,2022-06-01T00:00:00.000+00:00,zentao:ZentaoProject:1:1
+zentao:ZentaoExecution:1:12,TR5,",1091,12,",CLOSED,2022-07-07T00:00:00.000+00:00,,2022-11-03T00:00:00.000+00:00,zentao:ZentaoProject:1:1
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issue_changelogs.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issue_changelogs.csv
new file mode 100644
index 000000000..6779fab6a
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issue_changelogs.csv
@@ -0,0 +1,19 @@
+id,issue_id,author_id,author_name,field_id,field_name,original_from_value,original_to_value,from_value,to_value,created_date
+zentao:ZentaoChangelogDetail:1:112:68,1,0,admin,begin,begin,2012-06-05,2020-06-05,2012-06-05,2020-06-05,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:112:69,1,0,admin,end,end,2012-12-04,2021-12-04,2012-12-04,2021-12-04,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:112:70,1,0,admin,days,days,184,391,184,391,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:112:71,1,0,admin,status,status,done,wait,done,wait,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:113:72,2,0,admin,begin,begin,2013-06-05,2021-06-05,2013-06-05,2021-06-05,2021-04-28T11:08:02.000+00:00
+zentao:ZentaoChangelogDetail:1:113:73,2,0,admin,end,end,2014-06-04,2022-06-04,2014-06-04,2022-06-04,2021-04-28T11:08:02.000+00:00
+zentao:ZentaoChangelogDetail:1:113:74,2,0,admin,days,days,365,260,365,260,2021-04-28T11:08:02.000+00:00
+zentao:ZentaoChangelogDetail:1:114:75,1,0,admin,type,type,interface,codeerror,interface,codeerror,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:114:76,1,0,admin,pri,pri,0,1,0,1,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:115:77,2,0,admin,pri,pri,0,2,0,2,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:116:78,3,0,admin,pri,pri,0,1,0,1,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:117:79,4,0,admin,pri,pri,0,1,0,1,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:118:80,1,0,admin,build,build,trunk,1,trunk,1,2021-04-28T11:10:06.000+00:00
+zentao:ZentaoChangelogDetail:1:118:81,1,0,admin,begin,begin,2012-06-05,2020-06-05,2012-06-05,2020-06-05,2021-04-28T11:10:06.000+00:00
+zentao:ZentaoChangelogDetail:1:118:82,1,0,admin,end,end,2013-06-21,2021-06-21,2013-06-21,2021-06-21,2021-04-28T11:10:06.000+00:00
+zentao:ZentaoChangelogDetail:1:119:83,1,0,admin,begin,begin,2000-01-01,2020-06-05,2000-01-01,2020-06-05,2021-04-28T11:11:10.000+00:00
+zentao:ZentaoChangelogDetail:1:119:84,1,0,admin,end,end,2015-01-01,2021-06-04,2015-01-01,2021-06-04,2021-04-28T11:11:10.000+00:00
+zentao:ZentaoChangelogDetail:1:120:85,1,0,admin,status,status,wait,doing,wait,doing,2021-04-28T11:12:22.000+00:00
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
index 4c2746ff0..af603b91d 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
@@ -1,5 +1,5 @@
id,url,icon_url,issue_key,title,description,epic_key,type,original_type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
-zentao:ZentaoBug:1:1,,,1,首页页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:56:11.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:1,,0,0,0,7,测试甲,4,开发甲,,
-zentao:ZentaoBug:1:2,,,2,新闻中心页面问题,,,BUG,codeerror,IN_PROGRESS,delay,0,,2012-06-05T02:57:11.000+00:00,2022-10-05T04:19:22.000+00:00,0,zentao:ZentaoStory:1:2,,0,0,0,7,测试甲,0,,,
-zentao:ZentaoBug:1:3,,,3,成果展示页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:58:22.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:3,,0,0,0,8,测试乙,4,开发甲,,
-zentao:ZentaoBug:1:4,,,4,售后服务页面问题,,,BUG,codeerror,DONE,resolved,0,,2012-06-05T03:00:19.000+00:00,2022-10-05T04:10:08.000+00:00,0,zentao:ZentaoStory:1:4,,0,0,0,9,测试丙,9,测试丙,,
+zentao:ZentaoBug:1:1,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,1,首页页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:56:11.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:1,VeryHigh,0,0,0,7,测试甲,4,开发甲,,
+zentao:ZentaoBug:1:2,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,2,新闻中心页面问题,,,BUG,codeerror,IN_PROGRESS,delay,0,,2012-06-05T02:57:11.000+00:00,2022-10-05T04:19:22.000+00:00,0,zentao:ZentaoStory:1:2,High,0,0,0,7,测试甲,0,,,
+zentao:ZentaoBug:1:3,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,3,成果展示页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:58:22.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:3,VeryHigh,0,0,0,8,测试乙,4,开发甲,,
+zentao:ZentaoBug:1:4,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,4,售后服务页面问题,,,BUG,codeerror,DONE,resolved,0,,2012-06-05T03:00:19.000+00:00,2022-10-05T04:10:08.000+00:00,0,zentao:ZentaoStory:1:4,VeryHigh,0,0,0,9,测试丙,9,测试丙,,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
index 6e057c7f3..353971be5 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
@@ -1,8 +1,8 @@
id,url,icon_url,issue_key,title,description,epic_key,type,original_type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
-zentao:ZentaoStory:1:1,,,1,首页设计和开发,,,REQUIREMENT,story,IN_PROGRESS,developing,0,,2012-06-05T02:09:49.000+00:00,2012-06-05T02:25:19.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:2,,,2,新闻中心的设计和开发。,,,REQUIREMENT,story,IN_PROGRESS,projected,0,,2012-06-05T02:16:37.000+00:00,2012-06-05T02:25:33.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:3,,,3,成果展示的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,developing,0,,2012-06-05T02:18:10.000+00:00,2012-06-05T02:25:38.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:4,,,4,售后服务的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,developed,0,,2012-06-05T02:20:16.000+00:00,2012-06-05T02:25:42.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:5,,,5,诚聘英才的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,planned,0,,2012-06-05T02:21:39.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:6,,,6,合作洽谈的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,planned,0,,2012-06-05T02:23:11.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:7,,,7,关于我们的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,planned,0,,2012-06-05T02:24:19.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:1,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,1,首页设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,active-developing,0,,2012-06-05T02:09:49.000+00:00,2012-06-05T02:25:19.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:2,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,2,新闻中心的设计和开发。,,,REQUIREMENT,story.feature,IN_PROGRESS,active-projected,0,,2012-06-05T02:16:37.000+00:00,2012-06-05T02:25:33.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:3,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,3,成果展示的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,active-developing,0,,2012-06-05T02:18:10.000+00:00,2012-06-05T02:25:38.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:4,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,4,售后服务的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,active-developed,0,,2012-06-05T02:20:16.000+00:00,2012-06-05T02:25:42.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:5,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,5,诚聘英才的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,reviewing-planned,0,,2012-06-05T02:21:39.000+00:00,,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:6,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,6,合作洽谈的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,reviewing-planned,0,,2012-06-05T02:23:11.000+00:00,,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:7,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,7,关于我们的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,reviewing-planned,0,,2012-06-05T02:24:19.000+00:00,,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
b/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
index e54e48525..de0e33baa 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
@@ -1,4 +1,4 @@
id,url,icon_url,issue_key,title,description,epic_key,type,original_type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
-zentao:ZentaoTask:1:1,,,1,任务名称,任务描述<span> </span><br /><div><br
/></div>,,TASK,devel,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,1,devlake,5,开发乙,,
-zentao:ZentaoTask:1:2,,,2,任务名称,任务描述<span> </span><br /><div><br
/></div>,,TASK,devel,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,1,devlake,5,开发乙,,
-zentao:ZentaoTask:1:3,,,3,任务名称,任务描述<span> </span><br /><div><br
/></div>,,TASK,devel,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:-1,,0,0,0,1,devlake,5,开发乙,,
+zentao:ZentaoTask:1:1,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,,1,任务名称,任务描述<span>
</span><br /><div><br
/></div>,,TASK,devel.,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,Middle,0,0,0,1,devlake,5,开发乙,,
+zentao:ZentaoTask:1:2,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,,2,任务名称,任务描述<span>
</span><br /><div><br
/></div>,,TASK,devel.,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,Middle,0,0,0,1,devlake,5,开发乙,,
+zentao:ZentaoTask:1:3,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,,3,任务名称,任务描述<span>
</span><br /><div><br
/></div>,,TASK,devel.,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:-1,Middle,0,0,0,1,devlake,5,开发乙,,
diff --git a/backend/plugins/zentao/impl/impl.go
b/backend/plugins/zentao/impl/impl.go
index 1a725aebb..d10eb47ec 100644
--- a/backend/plugins/zentao/impl/impl.go
+++ b/backend/plugins/zentao/impl/impl.go
@@ -21,6 +21,7 @@ import (
"fmt"
"github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/core/runner"
@@ -57,22 +58,30 @@ func (p Zentao) SubTaskMetas() []plugin.SubTaskMeta {
return []plugin.SubTaskMeta{
tasks.ConvertProductMeta,
tasks.ConvertProjectMeta,
+
tasks.DBGetChangelogMeta,
+ tasks.ConvertChangelogMeta,
+
tasks.CollectExecutionMeta,
tasks.ExtractExecutionMeta,
tasks.ConvertExecutionMeta,
+
tasks.CollectStoryMeta,
tasks.ExtractStoryMeta,
tasks.ConvertStoryMeta,
+
tasks.CollectBugMeta,
tasks.ExtractBugMeta,
tasks.ConvertBugMeta,
+
tasks.CollectTaskMeta,
tasks.ExtractTaskMeta,
tasks.ConvertTaskMeta,
+
tasks.CollectAccountMeta,
tasks.ExtractAccountMeta,
tasks.ConvertAccountMeta,
+
tasks.CollectDepartmentMeta,
tasks.ExtractDepartmentMeta,
tasks.ConvertDepartmentMeta,
@@ -100,6 +109,18 @@ func (p Zentao) PrepareTaskData(taskCtx
plugin.TaskContext, options map[string]i
return nil, errors.Default.Wrap(err, "unable to get Zentao API
client instance: %v")
}
+ if op.ScopeConfigs == nil && op.ScopeConfigId != 0 {
+ var scopeConfig models.ZentaoScopeConfig
+ err = taskCtx.GetDal().First(&scopeConfig, dal.Where("id = ?",
op.ScopeConfigId))
+ if err != nil && taskCtx.GetDal().IsErrorNotFound(err) {
+ return nil, errors.BadInput.Wrap(err, "fail to get
ScopeConfigs")
+ }
+ op.ScopeConfigs, err = tasks.MakeScopeConfigs(scopeConfig)
+ if err != nil {
+ return nil, errors.BadInput.Wrap(err, "fail to make
ScopeConfigs")
+ }
+ }
+
data := &tasks.ZentaoTaskData{
Options: op,
ApiClient: apiClient,
diff --git a/backend/plugins/zentao/models/archived/scope_config.go
b/backend/plugins/zentao/models/archived/scope_config.go
new file mode 100644
index 000000000..5eb5bb0c9
--- /dev/null
+++ b/backend/plugins/zentao/models/archived/scope_config.go
@@ -0,0 +1,38 @@
+/*
+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 (
+ "encoding/json"
+
+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+)
+
+type ZentaoScopeConfig struct {
+ archived.ScopeConfig `mapstructure:",squash" json:",inline"
gorm:"embedded"`
+ ConnectionId uint64 `mapstructure:"connectionId"
json:"connectionId"`
+ Name string
`gorm:"type:varchar(255);index:idx_name_tapd,unique" validate:"required"
mapstructure:"name" json:"name"`
+ TypeMappings json.RawMessage
`mapstructure:"typeMappings,omitempty" json:"typeMappings"`
+ BugStatusMappings json.RawMessage
`mapstructure:"bugStatusMappings,omitempty" json:"bugStatusMappings"`
+ StoryStatusMappings json.RawMessage
`mapstructure:"storyStatusMappings,omitempty" json:"storyStatusMappings"`
+ TaskStatusMappings json.RawMessage
`mapstructure:"taskStatusMappings,omitempty" json:"taskStatusMappings"`
+}
+
+func (t ZentaoScopeConfig) TableName() string {
+ return "_tool_zentao_scope_configs"
+}
diff --git a/backend/plugins/zentao/models/bug.go
b/backend/plugins/zentao/models/bug.go
index 664910121..1a16bfa96 100644
--- a/backend/plugins/zentao/models/bug.go
+++ b/backend/plugins/zentao/models/bug.go
@@ -160,6 +160,9 @@ type ZentaoBug struct {
Needconfirm bool `json:"needconfirm"`
StatusName string `json:"statusName"`
ProductStatus string `json:"productStatus"`
+ Url string `json:"url"`
+ StdStatus string `json:"stdStatus"
gorm:"type:varchar(20)"`
+ StdType string `json:"stdType"
gorm:"type:varchar(20)"`
}
func (ZentaoBug) TableName() string {
diff --git
a/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
b/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
index ec8c676c6..dcdd79628 100644
---
a/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
+++
b/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
@@ -56,7 +56,7 @@ func (*addInitChangelogTables) Up(basicRes context.BasicRes)
errors.Error {
}
func (*addInitChangelogTables) Version() uint64 {
- return 20230525000001
+ return 20230530000002
}
func (*addInitChangelogTables) Name() string {
diff --git
a/backend/plugins/zentao/models/migrationscripts/20230601_add_scope_config.go
b/backend/plugins/zentao/models/migrationscripts/20230601_add_scope_config.go
new file mode 100644
index 000000000..3ed1bd37c
--- /dev/null
+++
b/backend/plugins/zentao/models/migrationscripts/20230601_add_scope_config.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 migrationscripts
+
+import (
+ "github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/helpers/migrationhelper"
+ "github.com/apache/incubator-devlake/plugins/zentao/models/archived"
+)
+
+type addScopeConfigTables struct{}
+
+type bug20230601 struct {
+ Url string `json:"url"`
+ StdStatus string `json:"stdStatus" gorm:"type:varchar(20)"`
+ StdType string `json:"stdType" gorm:"type:varchar(20)"`
+}
+
+func (bug20230601) TableName() string {
+ return "_tool_zentao_bugs"
+}
+
+type story20230601 struct {
+ Url string `json:"url"`
+ StdStatus string `json:"stdStatus" gorm:"type:varchar(20)"`
+ StdType string `json:"stdType" gorm:"type:varchar(20)"`
+}
+
+func (story20230601) TableName() string {
+ return "_tool_zentao_stories"
+}
+
+type task20230601 struct {
+ Url string `json:"url"`
+ StdStatus string `json:"stdStatus" gorm:"type:varchar(20)"`
+ StdType string `json:"stdType" gorm:"type:varchar(20)"`
+}
+
+func (task20230601) TableName() string {
+ return "_tool_zentao_tasks"
+}
+
+func (*addScopeConfigTables) Up(basicRes context.BasicRes) errors.Error {
+
+ return migrationhelper.AutoMigrateTables(
+ basicRes,
+ &archived.ZentaoScopeConfig{},
+ &bug20230601{},
+ &story20230601{},
+ &task20230601{},
+ )
+}
+
+func (*addScopeConfigTables) Version() uint64 {
+ return 20230601000001
+}
+
+func (*addScopeConfigTables) Name() string {
+ return "zentao add scope config tables"
+}
diff --git a/backend/plugins/zentao/models/migrationscripts/register.go
b/backend/plugins/zentao/models/migrationscripts/register.go
index e71dac9f4..df08cb6a2 100644
--- a/backend/plugins/zentao/models/migrationscripts/register.go
+++ b/backend/plugins/zentao/models/migrationscripts/register.go
@@ -27,5 +27,6 @@ func All() []plugin.MigrationScript {
new(addInitTables),
new(addInitChangelogTables),
new(addIssueCommitsTables),
+ new(addScopeConfigTables),
}
}
diff --git a/backend/plugins/zentao/models/remote_db.go
b/backend/plugins/zentao/models/remote_db.go
index bb5439458..a1b1f09fd 100644
--- a/backend/plugins/zentao/models/remote_db.go
+++ b/backend/plugins/zentao/models/remote_db.go
@@ -86,7 +86,7 @@ func (ah *ZentaoRemoteDbActionHistory) Convert()
*ZentaoChangelogCom {
},
&ZentaoChangelogDetail{
Id: int64(ah.HistoryId),
- ChangelogId: int64(ah.Id),
+ ChangelogId: int64(ah.ActionId),
Field: ah.Field,
Old: ah.Old,
New: ah.New,
diff --git a/backend/plugins/zentao/models/scope_config.go
b/backend/plugins/zentao/models/scope_config.go
new file mode 100644
index 000000000..d5a194e35
--- /dev/null
+++ b/backend/plugins/zentao/models/scope_config.go
@@ -0,0 +1,38 @@
+/*
+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 (
+ "encoding/json"
+
+ "github.com/apache/incubator-devlake/core/models/common"
+)
+
+type ZentaoScopeConfig struct {
+ common.ScopeConfig `mapstructure:",squash" json:",inline"
gorm:"embedded"`
+ ConnectionId uint64 `mapstructure:"connectionId"
json:"connectionId"`
+ Name string
`gorm:"type:varchar(255);index:idx_name_tapd,unique" validate:"required"
mapstructure:"name" json:"name"`
+ TypeMappings json.RawMessage
`mapstructure:"typeMappings,omitempty" json:"typeMappings"`
+ BugStatusMappings json.RawMessage
`mapstructure:"bugStatusMappings,omitempty" json:"bugStatusMappings"`
+ StoryStatusMappings json.RawMessage
`mapstructure:"storyStatusMappings,omitempty" json:"storyStatusMappings"`
+ TaskStatusMappings json.RawMessage
`mapstructure:"taskStatusMappings,omitempty" json:"taskStatusMappings"`
+}
+
+func (t ZentaoScopeConfig) TableName() string {
+ return "_tool_zentao_scope_configs"
+}
diff --git a/backend/plugins/zentao/models/story.go
b/backend/plugins/zentao/models/story.go
index 274fec596..84334f568 100644
--- a/backend/plugins/zentao/models/story.go
+++ b/backend/plugins/zentao/models/story.go
@@ -137,6 +137,9 @@ type ZentaoStory struct {
Deleted bool `json:"deleted"`
PriOrder string `json:"priOrder"`
PlanTitle string `json:"planTitle"`
+ Url string `json:"url"`
+ StdStatus string `json:"stdStatus"
gorm:"type:varchar(20)"`
+ StdType string `json:"stdType"
gorm:"type:varchar(20)"`
}
func (ZentaoStory) TableName() string {
diff --git a/backend/plugins/zentao/models/task.go
b/backend/plugins/zentao/models/task.go
index 43a20be93..18d3fc2f5 100644
--- a/backend/plugins/zentao/models/task.go
+++ b/backend/plugins/zentao/models/task.go
@@ -158,6 +158,9 @@ type ZentaoTask struct {
PriOrder string `json:"priOrder"`
NeedConfirm bool `json:"needConfirm"`
Progress float64 `json:"progress"`
+ Url string `json:"url"`
+ StdStatus string `json:"stdStatus"
gorm:"type:varchar(20)"`
+ StdType string `json:"stdType"
gorm:"type:varchar(20)"`
}
func (ZentaoTask) TableName() string {
diff --git a/backend/plugins/zentao/tasks/bug_convertor.go
b/backend/plugins/zentao/tasks/bug_convertor.go
index 3ae03828b..51fafc467 100644
--- a/backend/plugins/zentao/tasks/bug_convertor.go
+++ b/backend/plugins/zentao/tasks/bug_convertor.go
@@ -74,28 +74,26 @@ func ConvertBug(taskCtx plugin.SubTaskContext) errors.Error
{
DomainEntity: domainlayer.DomainEntity{
Id:
bugIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID),
},
- IssueKey:
strconv.FormatInt(toolEntity.ID, 10),
- Title: toolEntity.Title,
- Type: ticket.BUG,
- OriginalType: toolEntity.Type,
- OriginalStatus: toolEntity.Status,
- ResolutionDate:
toolEntity.ClosedDate.ToNullableTime(),
- CreatedDate:
toolEntity.OpenedDate.ToNullableTime(),
- UpdatedDate:
toolEntity.LastEditedDate.ToNullableTime(),
- ParentIssueId:
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Story),
- Priority: string(rune(toolEntity.Pri)),
- CreatorId:
strconv.FormatInt(toolEntity.OpenedById, 10),
- CreatorName: toolEntity.OpenedByName,
- AssigneeId:
strconv.FormatInt(toolEntity.AssignedToId, 10),
- AssigneeName: toolEntity.AssignedToName,
- Severity:
string(rune(toolEntity.Severity)),
- }
- switch toolEntity.Status {
- case "resolved":
- domainEntity.Status = ticket.DONE
- default:
- domainEntity.Status = ticket.IN_PROGRESS
+ IssueKey:
strconv.FormatInt(toolEntity.ID, 10),
+ Title: toolEntity.Title,
+ Type: ticket.BUG,
+ OriginalType: toolEntity.Type,
+ OriginalStatus: toolEntity.Status,
+ ResolutionDate:
toolEntity.ClosedDate.ToNullableTime(),
+ CreatedDate:
toolEntity.OpenedDate.ToNullableTime(),
+ UpdatedDate:
toolEntity.LastEditedDate.ToNullableTime(),
+ ParentIssueId:
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Story),
+ Priority: getPriority(toolEntity.Pri),
+ CreatorId:
strconv.FormatInt(toolEntity.OpenedById, 10),
+ CreatorName: toolEntity.OpenedByName,
+ AssigneeId:
strconv.FormatInt(toolEntity.AssignedToId, 10),
+ AssigneeName: toolEntity.AssignedToName,
+ Severity:
string(rune(toolEntity.Severity)),
+ Url: toolEntity.Url,
+ OriginalProject: getOriginalProject(data),
+ Status: toolEntity.StdStatus,
}
+
if toolEntity.ClosedDate != nil {
domainEntity.LeadTimeMinutes =
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
}
diff --git a/backend/plugins/zentao/tasks/bug_extractor.go
b/backend/plugins/zentao/tasks/bug_extractor.go
index eff39b734..3f3396d6e 100644
--- a/backend/plugins/zentao/tasks/bug_extractor.go
+++ b/backend/plugins/zentao/tasks/bug_extractor.go
@@ -21,6 +21,7 @@ import (
"encoding/json"
"github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
@@ -44,6 +45,9 @@ func ExtractBug(taskCtx plugin.SubTaskContext) errors.Error {
return nil
}
+ statusMappings := getBugStatusMapping(data)
+ stdTypeMappings := getStdTypeMappings(data)
+
extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
@@ -129,7 +133,23 @@ func ExtractBug(taskCtx plugin.SubTaskContext)
errors.Error {
Needconfirm: res.Needconfirm,
StatusName: res.StatusName,
ProductStatus: res.ProductStatus,
+ Url: row.Url,
+ }
+
+ bug.StdType = stdTypeMappings[bug.Type]
+ if bug.StdType == "" {
+ bug.StdType = ticket.BUG
}
+
+ if len(statusMappings) != 0 {
+ bug.StdStatus = statusMappings[bug.Status]
+ } else {
+ bug.StdStatus =
ticket.GetStatus(&ticket.StatusRule{
+ Done: []string{"resolved"},
+ Default: ticket.IN_PROGRESS,
+ }, bug.Status)
+ }
+
results := make([]interface{}, 0)
results = append(results, bug)
return results, nil
diff --git a/backend/plugins/zentao/tasks/changelog_convertor.go
b/backend/plugins/zentao/tasks/changelog_convertor.go
new file mode 100644
index 000000000..d67eaee0b
--- /dev/null
+++ b/backend/plugins/zentao/tasks/changelog_convertor.go
@@ -0,0 +1,131 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/common"
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ConvertChangelog
+
+var ConvertChangelogMeta = plugin.SubTaskMeta{
+ Name: "ConvertChangelog",
+ EntryPoint: ConvertChangelog,
+ EnabledByDefault: true,
+ Description: "convert Zentao changelog",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+type ZentaoChangelogSelect struct {
+ common.NoPKModel `json:"-"`
+
+ models.ZentaoChangelog
+ ChangelogId int64 `json:"changelogId" mapstructure:"changelogId"
gorm:"primaryKey;type:BIGINT NOT NULL"`
+ Field string `json:"field" mapstructure:"field"`
+ Old string `json:"old" mapstructure:"old"`
+ New string `json:"new" mapstructure:"new"`
+ Diff string `json:"diff" mapstructure:"diff"`
+
+ Account string `json:"account" gorm:"type:varchar(100);index"`
+ Avatar string `json:"avatar" gorm:"type:varchar(255)"`
+ Realname string `json:"realname" gorm:"type:varchar(100);index"`
+ Role string `json:"role" gorm:"type:varchar(100);index"`
+ Dept int64 `json:"dept" gorm:"type:BIGINT NOT NULL;index"`
+
+ CID int64 `json:"cid" mapstructure:"cid" gorm:"column:cid"`
+ CDID int64 `json:"cdid" mapstructure:"cdid" gorm:"column:cdid"`
+ AID int64 `json:"aid" mapstructure:"aid" gorm:"column:aid"`
+}
+
+func ConvertChangelog(taskCtx plugin.SubTaskContext) errors.Error {
+ data := taskCtx.GetData().(*ZentaoTaskData)
+ db := taskCtx.GetDal()
+ changelogIdGen :=
didgen.NewDomainIdGenerator(&models.ZentaoChangelogDetail{})
+ cn := models.ZentaoChangelog{}.TableName()
+ cdn := models.ZentaoChangelogDetail{}.TableName()
+ an := models.ZentaoAccount{}.TableName()
+ cursor, err := db.Cursor(
+ dal.Select(fmt.Sprintf("*,%s.id cid,%s.id cdid,%s.id aid ", cn,
cdn, an)),
+ dal.From(&models.ZentaoChangelog{}),
+ dal.Join(fmt.Sprintf("LEFT JOIN %s on %s.changelog_id = %s.id",
cdn, cdn, cn)),
+ dal.Join(fmt.Sprintf("LEFT JOIN %s on %s.realname = %s.actor",
an, an, cn)),
+ dal.Where(fmt.Sprintf(`%s.product = ? and %s.project = ? and
%s.connection_id = ?`,
+ cn, cn, cn),
+ data.Options.ProductId,
+ data.Options.ProjectId,
+ data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ convertor, err := api.NewDataConverter(api.DataConverterArgs{
+ InputRowType: reflect.TypeOf(ZentaoChangelogSelect{}),
+ Input: cursor,
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: ZentaoApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ ProductId: data.Options.ProductId,
+ ProjectId: data.Options.ProjectId,
+ },
+ Table: RAW_ACCOUNT_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
+ cl := inputRow.(*ZentaoChangelogSelect)
+
+ domainCl := &ticket.IssueChangelogs{
+ DomainEntity: domainlayer.DomainEntity{
+ Id:
changelogIdGen.Generate(data.Options.ConnectionId, cl.CID, cl.CDID),
+ },
+ IssueId: fmt.Sprintf("%d",
cl.ObjectId),
+ AuthorId: fmt.Sprintf("%d", cl.AID),
+ AuthorName: cl.Actor,
+ FieldId: cl.Field,
+ FieldName: cl.Field,
+ OriginalFromValue: cl.Old,
+ OriginalToValue: cl.New,
+ FromValue: cl.Old,
+ ToValue: cl.New,
+ CreatedDate: cl.Date,
+ }
+
+ return []interface{}{
+ domainCl,
+ }, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return convertor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/execution_convertor.go
b/backend/plugins/zentao/tasks/execution_convertor.go
index 4c9866189..735f86e4e 100644
--- a/backend/plugins/zentao/tasks/execution_convertor.go
+++ b/backend/plugins/zentao/tasks/execution_convertor.go
@@ -18,6 +18,8 @@ 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"
@@ -26,7 +28,6 @@ import (
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
- "reflect"
)
var _ plugin.SubTaskEntryPoint = ConvertExecutions
@@ -89,8 +90,8 @@ func ConvertExecutions(taskCtx plugin.SubTaskContext)
errors.Error {
Status: domainStatus,
StartedDate:
toolExecution.RealBegan.ToNullableTime(),
EndedDate:
toolExecution.RealEnd.ToNullableTime(),
- CompletedDate:
toolExecution.ClosedDate.ToNullableTime(),
- OriginalBoardID:
projectIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
+ CompletedDate:
toolExecution.PlanEnd.ToNullableTime(),
+ OriginalBoardID:
projectIdGen.Generate(toolExecution.ConnectionId, data.Options.ProjectId),
}
boardSprint := &ticket.BoardSprint{
BoardId:
projectIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
diff --git a/backend/plugins/zentao/tasks/product_convertor.go
b/backend/plugins/zentao/tasks/product_convertor.go
index 3ddfe4bd7..fc3a645eb 100644
--- a/backend/plugins/zentao/tasks/product_convertor.go
+++ b/backend/plugins/zentao/tasks/product_convertor.go
@@ -18,6 +18,7 @@ limitations under the License.
package tasks
import (
+ "fmt"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -69,6 +70,8 @@ func ConvertProducts(taskCtx plugin.SubTaskContext)
errors.Error {
Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
toolProduct := inputRow.(*models.ZentaoProduct)
+ data.ProductName = toolProduct.Name
+
domainBoard := &ticket.Board{
DomainEntity: domainlayer.DomainEntity{
Id:
boardIdGen.Generate(toolProduct.ConnectionId, toolProduct.Id),
@@ -77,6 +80,7 @@ func ConvertProducts(taskCtx plugin.SubTaskContext)
errors.Error {
Description: toolProduct.Description,
CreatedDate:
toolProduct.CreatedDate.ToNullableTime(),
Type: toolProduct.Type + "/" +
toolProduct.ProductType,
+ Url:
fmt.Sprintf("/product-index-%d.html", data.Options.ProductId),
}
results := make([]interface{}, 0)
results = append(results, domainBoard)
diff --git a/backend/plugins/zentao/tasks/project_convertor.go
b/backend/plugins/zentao/tasks/project_convertor.go
index 8bb555372..81f2fddfe 100644
--- a/backend/plugins/zentao/tasks/project_convertor.go
+++ b/backend/plugins/zentao/tasks/project_convertor.go
@@ -18,6 +18,7 @@ limitations under the License.
package tasks
import (
+ "fmt"
"reflect"
"github.com/apache/incubator-devlake/core/dal"
@@ -69,6 +70,8 @@ func ConvertProjects(taskCtx plugin.SubTaskContext)
errors.Error {
Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
toolProject := inputRow.(*models.ZentaoProject)
+ data.ProjectName = toolProject.Name
+
domainBoard := &ticket.Board{
DomainEntity: domainlayer.DomainEntity{
Id:
boardIdGen.Generate(toolProject.ConnectionId, toolProject.Id),
@@ -77,6 +80,7 @@ func ConvertProjects(taskCtx plugin.SubTaskContext)
errors.Error {
Description: toolProject.Description,
CreatedDate:
toolProject.OpenedDate.ToNullableTime(),
Type: toolProject.Type + "/" +
toolProject.ProjectType,
+ Url:
fmt.Sprintf("/project-index-%d.html", data.Options.ProjectId),
}
results := make([]interface{}, 0)
results = append(results, domainBoard)
diff --git a/backend/plugins/zentao/tasks/shared.go
b/backend/plugins/zentao/tasks/shared.go
index 3e71d34b5..d9fcc69c4 100644
--- a/backend/plugins/zentao/tasks/shared.go
+++ b/backend/plugins/zentao/tasks/shared.go
@@ -18,10 +18,12 @@ limitations under the License.
package tasks
import (
+ "net/http"
+ "strings"
+
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
- "net/http"
)
func GetTotalPagesFromResponse(res *http.Response, args *api.ApiCollectorArgs)
(int, errors.Error) {
@@ -50,3 +52,93 @@ func getAccountName(account *models.ZentaoAccount) string {
}
return ""
}
+
+// get the Priority string for zentao
+func getPriority(pri int) string {
+ switch pri {
+ case 2:
+ return "High"
+ case 3:
+ return "Middle"
+ case 4:
+ return "Low"
+ default:
+ if pri <= 1 {
+ return "VeryHigh"
+ }
+ if pri >= 5 {
+ return "VeryLow"
+ }
+ }
+ return "Error"
+}
+
+func getOriginalProject(data *ZentaoTaskData) string {
+ if data.Options.ProjectId != 0 {
+ return data.ProjectName
+ }
+ if data.Options.ProductId != 0 {
+ return data.ProductName
+ }
+ return ""
+}
+
+// getBugStatusMapping creates a map of original status values to bug issue
standard status values
+// based on the provided ZentaoTaskData. It returns the created map.
+func getBugStatusMapping(data *ZentaoTaskData) map[string]string {
+ stdStatusMappings := make(map[string]string)
+ if data.Options.ScopeConfigs == nil {
+ return stdStatusMappings
+ }
+ mapping := data.Options.ScopeConfigs.BugStatusMappings
+ // Map original status values to standard status values
+ for userStatus, stdStatus := range mapping {
+ stdStatusMappings[userStatus] = strings.ToUpper(stdStatus)
+ }
+ return stdStatusMappings
+}
+
+// getStoryStatusMapping creates a map of original status values to story
issue standard status values
+// based on the provided ZentaoTaskData. It returns the created map.
+func getStoryStatusMapping(data *ZentaoTaskData) map[string]string {
+ stdStatusMappings := make(map[string]string)
+ if data.Options.ScopeConfigs == nil {
+ return stdStatusMappings
+ }
+ mapping := data.Options.ScopeConfigs.StoryStatusMappings
+ // Map original status values to standard status values
+ for userStatus, stdStatus := range mapping {
+ stdStatusMappings[userStatus] = strings.ToUpper(stdStatus)
+ }
+ return stdStatusMappings
+}
+
+// getTaskStatusMapping creates a map of original status values to task issue
standard status values
+// based on the provided ZentaoTaskData. It returns the created map.
+func getTaskStatusMapping(data *ZentaoTaskData) map[string]string {
+ stdStatusMappings := make(map[string]string)
+ if data.Options.ScopeConfigs == nil {
+ return stdStatusMappings
+ }
+ mapping := data.Options.ScopeConfigs.TaskStatusMappings
+ // Map original status values to standard status values
+ for userStatus, stdStatus := range mapping {
+ stdStatusMappings[userStatus] = strings.ToUpper(stdStatus)
+ }
+ return stdStatusMappings
+}
+
+// getStdTypeMappings creates a map of user type to standard type based on the
provided ZentaoTaskData.
+// It returns the created map.
+func getStdTypeMappings(data *ZentaoTaskData) map[string]string {
+ stdTypeMappings := make(map[string]string)
+ if data.Options.ScopeConfigs == nil {
+ return stdTypeMappings
+ }
+ mapping := data.Options.ScopeConfigs.TypeMappings
+ // Map user types to standard types
+ for userType, stdType := range mapping {
+ stdTypeMappings[userType] = strings.ToUpper(stdType)
+ }
+ return stdTypeMappings
+}
diff --git a/backend/plugins/zentao/tasks/story_convertor.go
b/backend/plugins/zentao/tasks/story_convertor.go
index bfa9d3af5..cf1097e1b 100644
--- a/backend/plugins/zentao/tasks/story_convertor.go
+++ b/backend/plugins/zentao/tasks/story_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
package tasks
import (
+ "reflect"
+ "strconv"
+
"github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,8 +29,6 @@ import (
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
- "reflect"
- "strconv"
)
var _ plugin.SubTaskEntryPoint = ConvertStory
@@ -73,28 +74,27 @@ func ConvertStory(taskCtx plugin.SubTaskContext)
errors.Error {
DomainEntity: domainlayer.DomainEntity{
Id:
storyIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID),
},
- IssueKey:
strconv.FormatInt(toolEntity.ID, 10),
- Title: toolEntity.Title,
- Type: ticket.REQUIREMENT,
- OriginalType: toolEntity.Type,
- OriginalStatus: toolEntity.Stage,
- ResolutionDate:
toolEntity.ClosedDate.ToNullableTime(),
- CreatedDate:
toolEntity.OpenedDate.ToNullableTime(),
- UpdatedDate:
toolEntity.LastEditedDate.ToNullableTime(),
- ParentIssueId:
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
- Priority: string(rune(toolEntity.Pri)),
- CreatorId:
strconv.FormatInt(toolEntity.OpenedById, 10),
- CreatorName: toolEntity.OpenedByName,
- AssigneeId:
strconv.FormatInt(toolEntity.AssignedToId, 10),
- AssigneeName: toolEntity.AssignedToName,
+ IssueKey:
strconv.FormatInt(toolEntity.ID, 10),
+ Title: toolEntity.Title,
+ Type: ticket.REQUIREMENT,
+ OriginalType: toolEntity.Type + "." +
toolEntity.Category,
+ OriginalStatus: toolEntity.Status + "-" +
toolEntity.Stage,
+ ResolutionDate:
toolEntity.ClosedDate.ToNullableTime(),
+ CreatedDate:
toolEntity.OpenedDate.ToNullableTime(),
+ UpdatedDate:
toolEntity.LastEditedDate.ToNullableTime(),
+ ParentIssueId:
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
+ Priority: getPriority(toolEntity.Pri),
+ CreatorId:
strconv.FormatInt(toolEntity.OpenedById, 10),
+ CreatorName: toolEntity.OpenedByName,
+ AssigneeId:
strconv.FormatInt(toolEntity.AssignedToId, 10),
+ AssigneeName: toolEntity.AssignedToName,
+ Url: toolEntity.Url,
+ OriginalProject: getOriginalProject(data),
+ Status: toolEntity.StdStatus,
}
- switch toolEntity.Stage {
- case "closed":
- domainEntity.Status = ticket.DONE
- case "wait":
- domainEntity.Status = ticket.TODO
- default:
- domainEntity.Status = ticket.IN_PROGRESS
+
+ if domainEntity.OriginalStatus == "closed-closed" {
+ domainEntity.OriginalStatus = "closed"
}
var results []interface{}
if domainEntity.AssigneeId != "" {
@@ -105,6 +105,7 @@ func ConvertStory(taskCtx plugin.SubTaskContext)
errors.Error {
}
results = append(results, issueAssignee)
}
+
if toolEntity.ClosedDate != nil {
domainEntity.LeadTimeMinutes =
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
}
diff --git a/backend/plugins/zentao/tasks/story_extractor.go
b/backend/plugins/zentao/tasks/story_extractor.go
index e4e633fb5..46674fdd0 100644
--- a/backend/plugins/zentao/tasks/story_extractor.go
+++ b/backend/plugins/zentao/tasks/story_extractor.go
@@ -21,6 +21,7 @@ import (
"encoding/json"
"github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
@@ -44,6 +45,9 @@ func ExtractStory(taskCtx plugin.SubTaskContext) errors.Error
{
return nil
}
+ statusMappings := getStoryStatusMapping(data)
+ stdTypeMappings := getStdTypeMappings(data)
+
extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
@@ -116,7 +120,24 @@ func ExtractStory(taskCtx plugin.SubTaskContext)
errors.Error {
Deleted: res.Deleted,
PriOrder: res.PriOrder,
PlanTitle: res.PlanTitle,
+ Url: row.Url,
+ }
+
+ story.StdType = stdTypeMappings[story.Type]
+ if story.StdType == "" {
+ story.StdType = ticket.REQUIREMENT
}
+
+ if len(statusMappings) != 0 {
+ story.StdStatus = statusMappings[story.Stage]
+ } else {
+ story.StdStatus =
ticket.GetStatus(&ticket.StatusRule{
+ Done: []string{"closed"},
+ Todo: []string{"wait"},
+ Default: ticket.IN_PROGRESS,
+ }, story.Stage)
+ }
+
results := make([]interface{}, 0)
results = append(results, story)
return results, nil
diff --git a/backend/plugins/zentao/tasks/task_convertor.go
b/backend/plugins/zentao/tasks/task_convertor.go
index 22f44a9f4..75273c951 100644
--- a/backend/plugins/zentao/tasks/task_convertor.go
+++ b/backend/plugins/zentao/tasks/task_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
package tasks
import (
+ "reflect"
+ "strconv"
+
"github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,8 +29,6 @@ import (
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
- "reflect"
- "strconv"
)
var _ plugin.SubTaskEntryPoint = ConvertTask
@@ -73,30 +74,26 @@ func ConvertTask(taskCtx plugin.SubTaskContext)
errors.Error {
DomainEntity: domainlayer.DomainEntity{
Id:
taskIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID),
},
- IssueKey:
strconv.FormatInt(toolEntity.ID, 10),
- Title: toolEntity.Name,
- Description: toolEntity.Description,
- Type: ticket.TASK,
- OriginalType: toolEntity.Type,
- OriginalStatus: toolEntity.Status,
- ResolutionDate:
toolEntity.ClosedDate.ToNullableTime(),
- CreatedDate:
toolEntity.OpenedDate.ToNullableTime(),
- UpdatedDate:
toolEntity.LastEditedDate.ToNullableTime(),
- ParentIssueId:
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
- Priority: string(rune(toolEntity.Pri)),
- CreatorId:
strconv.FormatInt(toolEntity.OpenedById, 10),
- CreatorName: toolEntity.OpenedByName,
- AssigneeId:
strconv.FormatInt(toolEntity.AssignedToId, 10),
- AssigneeName: toolEntity.AssignedToName,
- }
- switch toolEntity.Status {
- case "done", "closed", "cancel":
- domainEntity.Status = ticket.DONE
- case "wait":
- domainEntity.Status = ticket.TODO
- default:
- domainEntity.Status = ticket.IN_PROGRESS
+ IssueKey:
strconv.FormatInt(toolEntity.ID, 10),
+ Title: toolEntity.Name,
+ Description: toolEntity.Description,
+ Type: ticket.TASK,
+ OriginalType: toolEntity.Type + "." +
toolEntity.Mode,
+ OriginalStatus: toolEntity.Status,
+ ResolutionDate:
toolEntity.ClosedDate.ToNullableTime(),
+ CreatedDate:
toolEntity.OpenedDate.ToNullableTime(),
+ UpdatedDate:
toolEntity.LastEditedDate.ToNullableTime(),
+ ParentIssueId:
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
+ Priority: getPriority(toolEntity.Pri),
+ CreatorId:
strconv.FormatInt(toolEntity.OpenedById, 10),
+ CreatorName: toolEntity.OpenedByName,
+ AssigneeId:
strconv.FormatInt(toolEntity.AssignedToId, 10),
+ AssigneeName: toolEntity.AssignedToName,
+ Url: toolEntity.Url,
+ OriginalProject: getOriginalProject(data),
+ Status: toolEntity.StdStatus,
}
+
if toolEntity.ClosedDate != nil {
domainEntity.LeadTimeMinutes =
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
}
diff --git a/backend/plugins/zentao/tasks/task_data.go
b/backend/plugins/zentao/tasks/task_data.go
index 9632e94f6..d406f4868 100644
--- a/backend/plugins/zentao/tasks/task_data.go
+++ b/backend/plugins/zentao/tasks/task_data.go
@@ -18,12 +18,14 @@ limitations under the License.
package tasks
import (
+ "encoding/json"
"fmt"
"time"
"github.com/apache/incubator-devlake/core/dal"
"github.com/apache/incubator-devlake/core/errors"
helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/zentao/models"
"github.com/mitchellh/mapstructure"
)
@@ -41,17 +43,69 @@ type ZentaoOptions struct {
ProductId int64 `json:"productId" mapstructure:"productId"`
ProjectId int64 `json:"projectId" mapstructure:"projectId"`
// TODO not support now
- TimeAfter string `json:"timeAfter" mapstructure:"timeAfter,omitempty"`
- //TransformationRuleId uint64
`json:"transformationZentaoeId" mapstructure:"transformationRuleId,omitempty"`
- //*models.ZentaoTransformationRule
`mapstructure:"transformationRules,omitempty" json:"transformationRules"`
+ TimeAfter string `json:"timeAfter"
mapstructure:"timeAfter,omitempty"`
+ ScopeConfigId uint64 `json:"scopeConfigId"
mapstructure:"scopeConfigId,omitempty"`
+ ScopeConfigs *ZentaoScopeConfigs `json:"scopeConfigs"
mapstructure:"scopeConfigs,omitempty"`
+}
+
+type TypeMappings map[string]string
+
+type StatusMappings map[string]string
+type ZentaoScopeConfigs struct {
+ TypeMappings TypeMappings `json:"typeMappings"`
+ BugStatusMappings StatusMappings `json:"bugStatusMappings"`
+ StoryStatusMappings StatusMappings `json:"storyStatusMappings"`
+ TaskStatusMappings StatusMappings `json:"taskStatusMappings"`
+}
+
+func MakeScopeConfigs(rule models.ZentaoScopeConfig) (*ZentaoScopeConfigs,
errors.Error) {
+ var bugStatusMapping StatusMappings
+ var storyStatusMapping StatusMappings
+ var taskStatusMapping StatusMappings
+ var typeMapping TypeMappings
+ var err error
+ if len(rule.TypeMappings) > 0 {
+ err = json.Unmarshal(rule.TypeMappings, &typeMapping)
+ if err != nil {
+ return nil, errors.Default.Wrap(err, "unable to
unmarshal the typeMapping")
+ }
+ }
+ if len(rule.BugStatusMappings) > 0 {
+ err = json.Unmarshal(rule.BugStatusMappings, &bugStatusMapping)
+ if err != nil {
+ return nil, errors.Default.Wrap(err, "unable to
unmarshal the statusMapping")
+ }
+ }
+ if len(rule.StoryStatusMappings) > 0 {
+ err = json.Unmarshal(rule.StoryStatusMappings,
&storyStatusMapping)
+ if err != nil {
+ return nil, errors.Default.Wrap(err, "unable to
unmarshal the statusMapping")
+ }
+ }
+ if len(rule.TaskStatusMappings) > 0 {
+ err = json.Unmarshal(rule.TaskStatusMappings,
&taskStatusMapping)
+ if err != nil {
+ return nil, errors.Default.Wrap(err, "unable to
unmarshal the statusMapping")
+ }
+ }
+ result := &ZentaoScopeConfigs{
+ TypeMappings: typeMapping,
+ BugStatusMappings: bugStatusMapping,
+ StoryStatusMappings: storyStatusMapping,
+ TaskStatusMappings: taskStatusMapping,
+ }
+ return result, nil
}
type ZentaoTaskData struct {
- Options *ZentaoOptions
- RemoteDb dal.Dal
- TimeAfter *time.Time
- ApiClient *helper.ApiAsyncClient
+ Options *ZentaoOptions
+ RemoteDb dal.Dal
+
+ TimeAfter *time.Time
+ ProjectName string
+ ProductName string
+ ApiClient *helper.ApiAsyncClient
}
func DecodeAndValidateTaskOptions(options map[string]interface{})
(*ZentaoOptions, error) {
diff --git a/backend/plugins/zentao/tasks/task_extractor.go
b/backend/plugins/zentao/tasks/task_extractor.go
index 7d6efc18a..c1935f7e9 100644
--- a/backend/plugins/zentao/tasks/task_extractor.go
+++ b/backend/plugins/zentao/tasks/task_extractor.go
@@ -21,6 +21,7 @@ import (
"encoding/json"
"github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/zentao/models"
@@ -44,6 +45,9 @@ func ExtractTask(taskCtx plugin.SubTaskContext) errors.Error {
return nil
}
+ statusMappings := getTaskStatusMapping(data)
+ stdTypeMappings := getStdTypeMappings(data)
+
extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
RawDataSubTaskArgs: api.RawDataSubTaskArgs{
Ctx: taskCtx,
@@ -128,7 +132,24 @@ func ExtractTask(taskCtx plugin.SubTaskContext)
errors.Error {
NeedConfirm: res.NeedConfirm,
//ProductType: res.ProductType,
Progress: res.Progress,
+ Url: row.Url,
+ }
+
+ task.StdType = stdTypeMappings[task.Type]
+ if task.StdType == "" {
+ task.StdType = ticket.TASK
}
+
+ if len(statusMappings) != 0 {
+ task.StdStatus = statusMappings[task.Status]
+ } else {
+ task.StdStatus =
ticket.GetStatus(&ticket.StatusRule{
+ Done: []string{"done", "closed",
"cancel"},
+ Todo: []string{"wait"},
+ Default: ticket.IN_PROGRESS,
+ }, task.Status)
+ }
+
results := make([]interface{}, 0)
results = append(results, task)
return results, nil