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 <124539898+gosimplic...@users.noreply.github.com>
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},
+}

Reply via email to