Skip to content

Commit 2ef56cf

Browse files
authored
feat(tapd): add the ability to get tapd lifetime (#8369)
* feat: add the ability to get tapd lifetime * fix: add task
1 parent cf7c14e commit 2ef56cf

File tree

6 files changed

+276
-0
lines changed

6 files changed

+276
-0
lines changed

backend/plugins/tapd/impl/impl.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func (p Tapd) GetTablesInfo() []dal.Tabler {
105105
&models.TapdBugCustomFieldValue{},
106106
&models.TapdScopeConfig{},
107107
&models.TapdWorkitemType{},
108+
&models.TapdLifeTime{},
108109
}
109110
}
110111

@@ -181,6 +182,8 @@ func (p Tapd) SubTaskMetas() []plugin.SubTaskMeta {
181182
tasks.EnrichStoryCustomFieldMeta,
182183
tasks.EnrichBugCustomFieldMeta,
183184
tasks.EnrichTaskCustomFieldMeta,
185+
tasks.CollectLifeTimesMeta,
186+
tasks.ExtractLifeTimesMeta,
184187
}
185188
}
186189

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package models
19+
20+
import "github.com/apache/incubator-devlake/core/models/common"
21+
22+
type TapdLifeTime struct {
23+
ConnectionId uint64 `gorm:"primaryKey"`
24+
Id uint64 `gorm:"primaryKey;type:BIGINT NOT NULL;autoIncrement:false" json:"id,string"`
25+
WorkspaceId uint64 `json:"workspace_id,string"`
26+
EntityType string `json:"entity_type" gorm:"type:varchar(255)"`
27+
EntityId uint64 `json:"entity_id,string"`
28+
Status string `json:"status" gorm:"type:varchar(255)"`
29+
Owner string `json:"owner" gorm:"type:varchar(255)"`
30+
BeginDate *common.CSTTime `json:"begin_date"`
31+
EndDate *common.CSTTime `json:"end_date"`
32+
TimeCost float64 `json:"time_cost,string"`
33+
Created *common.CSTTime `json:"created"`
34+
Operator string `json:"operator" gorm:"type:varchar(255)"`
35+
IsRepeated int `json:"is_repeated,string"`
36+
ChangeFrom string `json:"change_from" gorm:"type:varchar(255)"`
37+
common.NoPKModel
38+
}
39+
40+
func (TapdLifeTime) TableName() string {
41+
return "_tool_tapd_life_times"
42+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package migrationscripts
19+
20+
import (
21+
"time"
22+
23+
"github.com/apache/incubator-devlake/core/context"
24+
"github.com/apache/incubator-devlake/core/errors"
25+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
26+
"github.com/apache/incubator-devlake/core/plugin"
27+
"github.com/apache/incubator-devlake/helpers/migrationhelper"
28+
)
29+
30+
type TapdLifeTime struct {
31+
ConnectionId uint64 `gorm:"primaryKey"`
32+
Id uint64 `gorm:"primaryKey;type:BIGINT NOT NULL;autoIncrement:false" json:"id,string"`
33+
WorkspaceId uint64 `json:"workspace_id,string"`
34+
EntityType string `json:"entity_type" gorm:"type:varchar(255)"`
35+
EntityId uint64 `json:"entity_id,string"`
36+
Status string `json:"status" gorm:"type:varchar(255)"`
37+
Owner string `json:"owner" gorm:"type:varchar(255)"`
38+
BeginDate *time.Time `json:"begin_date"`
39+
EndDate *time.Time `json:"end_date"`
40+
TimeCost float64 `json:"time_cost"`
41+
Created *time.Time `json:"created"`
42+
Operator string `json:"operator" gorm:"type:varchar(255)"`
43+
IsRepeated int `json:"is_repeated"`
44+
ChangeFrom string `json:"change_from" gorm:"type:varchar(255)"`
45+
archived.NoPKModel
46+
}
47+
48+
func (TapdLifeTime) TableName() string {
49+
return "_tool_tapd_life_times"
50+
}
51+
52+
var _ plugin.MigrationScript = (*addLifetimeTables)(nil)
53+
54+
type addLifetimeTables struct{}
55+
56+
func (*addLifetimeTables) Up(basicRes context.BasicRes) errors.Error {
57+
return migrationhelper.AutoMigrateTables(basicRes, &TapdLifeTime{})
58+
}
59+
60+
func (*addLifetimeTables) Version() uint64 {
61+
return 20250221000000
62+
}
63+
64+
func (*addLifetimeTables) Name() string {
65+
return "add tapd lifetime tables"
66+
}

backend/plugins/tapd/models/migrationscripts/register.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ func All() []plugin.MigrationScript {
3535
new(addConnIdToLabels),
3636
new(addCompanyIdToConnection),
3737
new(updateScopeConfig20250305),
38+
new(addLifetimeTables),
3839
}
3940
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package tasks
19+
20+
import (
21+
"fmt"
22+
"net/url"
23+
"reflect"
24+
25+
"github.com/apache/incubator-devlake/core/dal"
26+
"github.com/apache/incubator-devlake/core/errors"
27+
"github.com/apache/incubator-devlake/core/plugin"
28+
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
29+
"github.com/apache/incubator-devlake/plugins/tapd/models"
30+
)
31+
32+
const RAW_LIFE_TIME_TABLE = "tapd_api_life_times"
33+
34+
var _ plugin.SubTaskEntryPoint = CollectLifeTimes
35+
36+
func CollectLifeTimes(taskCtx plugin.SubTaskContext) errors.Error {
37+
rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_LIFE_TIME_TABLE)
38+
db := taskCtx.GetDal()
39+
apiCollector, err := api.NewStatefulApiCollector(*rawDataSubTaskArgs)
40+
if err != nil {
41+
return err
42+
}
43+
logger := taskCtx.GetLogger()
44+
logger.Info("collect lifeTimes")
45+
46+
clauses := []dal.Clause{
47+
dal.Select("id as issue_id, modified as update_time"),
48+
dal.From(&models.TapdStory{}),
49+
dal.Where("connection_id = ? AND workspace_id = ?", data.Options.ConnectionId, data.Options.WorkspaceId),
50+
}
51+
if apiCollector.IsIncremental() && apiCollector.GetSince() != nil {
52+
clauses = append(clauses, dal.Where("modified > ?", *apiCollector.GetSince()))
53+
}
54+
55+
cursor, err := db.Cursor(clauses...)
56+
if err != nil {
57+
return err
58+
}
59+
iterator, err := api.NewDalCursorIterator(db, cursor, reflect.TypeOf(models.Input{}))
60+
if err != nil {
61+
return err
62+
}
63+
err = apiCollector.InitCollector(api.ApiCollectorArgs{
64+
ApiClient: data.ApiClient,
65+
Input: iterator,
66+
UrlTemplate: "life_times",
67+
Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
68+
input := reqData.Input.(*models.Input)
69+
query := url.Values{}
70+
query.Set("workspace_id", fmt.Sprintf("%v", data.Options.WorkspaceId))
71+
query.Set("entity_type", "story")
72+
query.Set("entity_id", fmt.Sprintf("%v", input.IssueId))
73+
return query, nil
74+
},
75+
ResponseParser: GetRawMessageArrayFromResponse,
76+
})
77+
if err != nil {
78+
logger.Error(err, "collect lifeTime error")
79+
return err
80+
}
81+
return apiCollector.Execute()
82+
}
83+
84+
var CollectLifeTimesMeta = plugin.SubTaskMeta{
85+
Name: "CollectLifeTimes",
86+
EntryPoint: CollectLifeTimes,
87+
EnabledByDefault: true,
88+
Description: "convert Tapd life times",
89+
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
90+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package tasks
19+
20+
import (
21+
"encoding/json"
22+
23+
"github.com/apache/incubator-devlake/core/errors"
24+
"github.com/apache/incubator-devlake/core/plugin"
25+
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
26+
"github.com/apache/incubator-devlake/plugins/tapd/models"
27+
)
28+
29+
func ExtractLifeTimes(taskCtx plugin.SubTaskContext) errors.Error {
30+
rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_LIFE_TIME_TABLE)
31+
rep, err := api.NewApiExtractor(api.ApiExtractorArgs{
32+
RawDataSubTaskArgs: *rawDataSubTaskArgs,
33+
Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
34+
var rawData struct {
35+
LifeTime models.TapdLifeTime `json:"LifeTime"`
36+
}
37+
err := json.Unmarshal([]byte(row.Data), &rawData)
38+
if err != nil {
39+
return nil, errors.Convert(err)
40+
}
41+
42+
toolLifetime := &models.TapdLifeTime{
43+
ConnectionId: data.Options.ConnectionId,
44+
WorkspaceId: data.Options.WorkspaceId,
45+
Id: rawData.LifeTime.Id,
46+
EntityType: rawData.LifeTime.EntityType,
47+
EntityId: rawData.LifeTime.EntityId,
48+
Status: rawData.LifeTime.Status,
49+
Owner: rawData.LifeTime.Owner,
50+
BeginDate: rawData.LifeTime.BeginDate,
51+
EndDate: rawData.LifeTime.EndDate,
52+
TimeCost: rawData.LifeTime.TimeCost,
53+
Created: rawData.LifeTime.Created,
54+
Operator: rawData.LifeTime.Operator,
55+
IsRepeated: rawData.LifeTime.IsRepeated,
56+
}
57+
return []interface{}{toolLifetime}, nil
58+
},
59+
})
60+
61+
if err != nil {
62+
return err
63+
}
64+
65+
return rep.Execute()
66+
}
67+
68+
var ExtractLifeTimesMeta = plugin.SubTaskMeta{
69+
Name: "extractLifeTimes",
70+
EntryPoint: ExtractLifeTimes,
71+
EnabledByDefault: true,
72+
Description: "extract Tapd life times",
73+
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
74+
}

0 commit comments

Comments
 (0)