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 2ef56cf76 feat(tapd): add the ability to get tapd lifetime (#8369)
2ef56cf76 is described below
commit 2ef56cf767fdee57f8f75b292f69bc5325cc5eaa
Author: Bamboo <[email protected]>
AuthorDate: Mon Jun 16 11:39:11 2025 +0800
feat(tapd): add the ability to get tapd lifetime (#8369)
* feat: add the ability to get tapd lifetime
* fix: add task
---
backend/plugins/tapd/impl/impl.go | 3 +
backend/plugins/tapd/models/life_time.go | 42 ++++++++++
.../20250221_add_lifetime_tables.go | 66 ++++++++++++++++
.../tapd/models/migrationscripts/register.go | 1 +
.../tapd/tasks/workitem_lifetime_collector.go | 90 ++++++++++++++++++++++
.../tapd/tasks/workitem_lifetime_extractor.go | 74 ++++++++++++++++++
6 files changed, 276 insertions(+)
diff --git a/backend/plugins/tapd/impl/impl.go
b/backend/plugins/tapd/impl/impl.go
index 9f3e932ea..a73130b86 100644
--- a/backend/plugins/tapd/impl/impl.go
+++ b/backend/plugins/tapd/impl/impl.go
@@ -105,6 +105,7 @@ func (p Tapd) GetTablesInfo() []dal.Tabler {
&models.TapdBugCustomFieldValue{},
&models.TapdScopeConfig{},
&models.TapdWorkitemType{},
+ &models.TapdLifeTime{},
}
}
@@ -181,6 +182,8 @@ func (p Tapd) SubTaskMetas() []plugin.SubTaskMeta {
tasks.EnrichStoryCustomFieldMeta,
tasks.EnrichBugCustomFieldMeta,
tasks.EnrichTaskCustomFieldMeta,
+ tasks.CollectLifeTimesMeta,
+ tasks.ExtractLifeTimesMeta,
}
}
diff --git a/backend/plugins/tapd/models/life_time.go
b/backend/plugins/tapd/models/life_time.go
new file mode 100644
index 000000000..a1187f847
--- /dev/null
+++ b/backend/plugins/tapd/models/life_time.go
@@ -0,0 +1,42 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package models
+
+import "github.com/apache/incubator-devlake/core/models/common"
+
+type TapdLifeTime struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ Id uint64 `gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false" json:"id,string"`
+ WorkspaceId uint64 `json:"workspace_id,string"`
+ EntityType string `json:"entity_type"
gorm:"type:varchar(255)"`
+ EntityId uint64 `json:"entity_id,string"`
+ Status string `json:"status" gorm:"type:varchar(255)"`
+ Owner string `json:"owner" gorm:"type:varchar(255)"`
+ BeginDate *common.CSTTime `json:"begin_date"`
+ EndDate *common.CSTTime `json:"end_date"`
+ TimeCost float64 `json:"time_cost,string"`
+ Created *common.CSTTime `json:"created"`
+ Operator string `json:"operator" gorm:"type:varchar(255)"`
+ IsRepeated int `json:"is_repeated,string"`
+ ChangeFrom string `json:"change_from"
gorm:"type:varchar(255)"`
+ common.NoPKModel
+}
+
+func (TapdLifeTime) TableName() string {
+ return "_tool_tapd_life_times"
+}
diff --git
a/backend/plugins/tapd/models/migrationscripts/20250221_add_lifetime_tables.go
b/backend/plugins/tapd/models/migrationscripts/20250221_add_lifetime_tables.go
new file mode 100644
index 000000000..157848fb9
--- /dev/null
+++
b/backend/plugins/tapd/models/migrationscripts/20250221_add_lifetime_tables.go
@@ -0,0 +1,66 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/errors"
+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/migrationhelper"
+)
+
+type TapdLifeTime struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ Id uint64 `gorm:"primaryKey;type:BIGINT NOT
NULL;autoIncrement:false" json:"id,string"`
+ WorkspaceId uint64 `json:"workspace_id,string"`
+ EntityType string `json:"entity_type" gorm:"type:varchar(255)"`
+ EntityId uint64 `json:"entity_id,string"`
+ Status string `json:"status" gorm:"type:varchar(255)"`
+ Owner string `json:"owner" gorm:"type:varchar(255)"`
+ BeginDate *time.Time `json:"begin_date"`
+ EndDate *time.Time `json:"end_date"`
+ TimeCost float64 `json:"time_cost"`
+ Created *time.Time `json:"created"`
+ Operator string `json:"operator" gorm:"type:varchar(255)"`
+ IsRepeated int `json:"is_repeated"`
+ ChangeFrom string `json:"change_from" gorm:"type:varchar(255)"`
+ archived.NoPKModel
+}
+
+func (TapdLifeTime) TableName() string {
+ return "_tool_tapd_life_times"
+}
+
+var _ plugin.MigrationScript = (*addLifetimeTables)(nil)
+
+type addLifetimeTables struct{}
+
+func (*addLifetimeTables) Up(basicRes context.BasicRes) errors.Error {
+ return migrationhelper.AutoMigrateTables(basicRes, &TapdLifeTime{})
+}
+
+func (*addLifetimeTables) Version() uint64 {
+ return 20250221000000
+}
+
+func (*addLifetimeTables) Name() string {
+ return "add tapd lifetime tables"
+}
diff --git a/backend/plugins/tapd/models/migrationscripts/register.go
b/backend/plugins/tapd/models/migrationscripts/register.go
index 206cbf297..18b4fe87e 100644
--- a/backend/plugins/tapd/models/migrationscripts/register.go
+++ b/backend/plugins/tapd/models/migrationscripts/register.go
@@ -35,5 +35,6 @@ func All() []plugin.MigrationScript {
new(addConnIdToLabels),
new(addCompanyIdToConnection),
new(updateScopeConfig20250305),
+ new(addLifetimeTables),
}
}
diff --git a/backend/plugins/tapd/tasks/workitem_lifetime_collector.go
b/backend/plugins/tapd/tasks/workitem_lifetime_collector.go
new file mode 100644
index 000000000..183b6f868
--- /dev/null
+++ b/backend/plugins/tapd/tasks/workitem_lifetime_collector.go
@@ -0,0 +1,90 @@
+/*
+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"
+ "net/url"
+ "reflect"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/tapd/models"
+)
+
+const RAW_LIFE_TIME_TABLE = "tapd_api_life_times"
+
+var _ plugin.SubTaskEntryPoint = CollectLifeTimes
+
+func CollectLifeTimes(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_LIFE_TIME_TABLE)
+ db := taskCtx.GetDal()
+ apiCollector, err := api.NewStatefulApiCollector(*rawDataSubTaskArgs)
+ if err != nil {
+ return err
+ }
+ logger := taskCtx.GetLogger()
+ logger.Info("collect lifeTimes")
+
+ clauses := []dal.Clause{
+ dal.Select("id as issue_id, modified as update_time"),
+ dal.From(&models.TapdStory{}),
+ dal.Where("connection_id = ? AND workspace_id = ?",
data.Options.ConnectionId, data.Options.WorkspaceId),
+ }
+ if apiCollector.IsIncremental() && apiCollector.GetSince() != nil {
+ clauses = append(clauses, dal.Where("modified > ?",
*apiCollector.GetSince()))
+ }
+
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return err
+ }
+ iterator, err := api.NewDalCursorIterator(db, cursor,
reflect.TypeOf(models.Input{}))
+ if err != nil {
+ return err
+ }
+ err = apiCollector.InitCollector(api.ApiCollectorArgs{
+ ApiClient: data.ApiClient,
+ Input: iterator,
+ UrlTemplate: "life_times",
+ Query: func(reqData *api.RequestData) (url.Values,
errors.Error) {
+ input := reqData.Input.(*models.Input)
+ query := url.Values{}
+ query.Set("workspace_id", fmt.Sprintf("%v",
data.Options.WorkspaceId))
+ query.Set("entity_type", "story")
+ query.Set("entity_id", fmt.Sprintf("%v", input.IssueId))
+ return query, nil
+ },
+ ResponseParser: GetRawMessageArrayFromResponse,
+ })
+ if err != nil {
+ logger.Error(err, "collect lifeTime error")
+ return err
+ }
+ return apiCollector.Execute()
+}
+
+var CollectLifeTimesMeta = plugin.SubTaskMeta{
+ Name: "CollectLifeTimes",
+ EntryPoint: CollectLifeTimes,
+ EnabledByDefault: true,
+ Description: "convert Tapd life times",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}
diff --git a/backend/plugins/tapd/tasks/workitem_lifetime_extractor.go
b/backend/plugins/tapd/tasks/workitem_lifetime_extractor.go
new file mode 100644
index 000000000..746cd9bb0
--- /dev/null
+++ b/backend/plugins/tapd/tasks/workitem_lifetime_extractor.go
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/tapd/models"
+)
+
+func ExtractLifeTimes(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_LIFE_TIME_TABLE)
+ rep, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ var rawData struct {
+ LifeTime models.TapdLifeTime `json:"LifeTime"`
+ }
+ err := json.Unmarshal([]byte(row.Data), &rawData)
+ if err != nil {
+ return nil, errors.Convert(err)
+ }
+
+ toolLifetime := &models.TapdLifeTime{
+ ConnectionId: data.Options.ConnectionId,
+ WorkspaceId: data.Options.WorkspaceId,
+ Id: rawData.LifeTime.Id,
+ EntityType: rawData.LifeTime.EntityType,
+ EntityId: rawData.LifeTime.EntityId,
+ Status: rawData.LifeTime.Status,
+ Owner: rawData.LifeTime.Owner,
+ BeginDate: rawData.LifeTime.BeginDate,
+ EndDate: rawData.LifeTime.EndDate,
+ TimeCost: rawData.LifeTime.TimeCost,
+ Created: rawData.LifeTime.Created,
+ Operator: rawData.LifeTime.Operator,
+ IsRepeated: rawData.LifeTime.IsRepeated,
+ }
+ return []interface{}{toolLifetime}, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return rep.Execute()
+}
+
+var ExtractLifeTimesMeta = plugin.SubTaskMeta{
+ Name: "extractLifeTimes",
+ EntryPoint: ExtractLifeTimes,
+ EnabledByDefault: true,
+ Description: "extract Tapd life times",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
+}