This is an automated email from the ASF dual-hosted git repository.

warren 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 83c582af2 feat(bamboo): add job build convertor (#4470)
83c582af2 is described below

commit 83c582af2c9ce33b843e75a59aa70b052595f1d4
Author: Warren Chen <[email protected]>
AuthorDate: Tue Feb 21 22:11:17 2023 +0800

    feat(bamboo): add job build convertor (#4470)
---
 backend/plugins/bamboo/api/transformation_rule.go  | 132 +++++++++++++++++++++
 backend/plugins/bamboo/bamboo.go                   |   2 +-
 backend/plugins/bamboo/impl/impl.go                |  12 +-
 backend/plugins/bamboo/models/connection.go        |  10 +-
 backend/plugins/bamboo/models/job_build.go         |  77 ++++++------
 .../migrationscripts/20230216_add_init_tables.go   |   4 +-
 .../models/migrationscripts/archived/connection.go |   6 +-
 .../models/migrationscripts/archived/job_build.go  |  77 ++++++------
 .../models/migrationscripts/archived/plan_build.go |  54 ++++-----
 .../models/migrationscripts/archived/project.go    |  13 +-
 .../archived}/transformation_rule.go               |  13 +-
 backend/plugins/bamboo/models/plan_build.go        | 108 ++++++++++-------
 .../plugins/bamboo/models/transformation_rule.go   |   7 +-
 .../plugins/bamboo/tasks/job_build_convertor.go    | 102 ++++++++++++++++
 .../plugins/bamboo/tasks/job_build_extractor.go    |  18 ++-
 .../plugins/bamboo/tasks/plan_build_extractor.go   |   1 +
 16 files changed, 466 insertions(+), 170 deletions(-)

diff --git a/backend/plugins/bamboo/api/transformation_rule.go 
b/backend/plugins/bamboo/api/transformation_rule.go
new file mode 100644
index 000000000..d2115d86d
--- /dev/null
+++ b/backend/plugins/bamboo/api/transformation_rule.go
@@ -0,0 +1,132 @@
+/*
+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/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/bamboo/models"
+       "net/http"
+       "strconv"
+)
+
+// CreateTransformationRule create transformation rule for Bamboo
+// @Summary create transformation rule for Bamboo
+// @Description create transformation rule for Bamboo
+// @Tags plugins/bamboo
+// @Accept application/json
+// @Param transformationRule body models.BambooTransformationRule true 
"transformation rule"
+// @Success 200  {object} models.BambooTransformationRule
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/bamboo/transformation_rules [POST]
+func CreateTransformationRule(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       var rule models.BambooTransformationRule
+       err := api.Decode(input.Body, &rule, vld)
+       if err != nil {
+               return nil, errors.BadInput.Wrap(err, "error in decoding 
transformation rule")
+       }
+       err = basicRes.GetDal().Create(&rule)
+       if err != nil {
+               if basicRes.GetDal().IsDuplicationError(err) {
+                       return nil, errors.BadInput.New("there was a 
transformation rule with the same name, please choose another name")
+               }
+               return nil, errors.BadInput.Wrap(err, "error on saving 
TransformationRule")
+       }
+       return &plugin.ApiResourceOutput{Body: rule, Status: http.StatusOK}, nil
+}
+
+// UpdateTransformationRule update transformation rule for Bamboo
+// @Summary update transformation rule for Bamboo
+// @Description update transformation rule for Bamboo
+// @Tags plugins/bamboo
+// @Accept application/json
+// @Param id path int true "id"
+// @Param transformationRule body models.BambooTransformationRule true 
"transformation rule"
+// @Success 200  {object} models.BambooTransformationRule
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/bamboo/transformation_rules/{id} [PATCH]
+func UpdateTransformationRule(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       transformationRuleId, e := strconv.ParseUint(input.Params["id"], 10, 64)
+       if e != nil {
+               return nil, errors.Default.Wrap(e, "the transformation rule ID 
should be an integer")
+       }
+       var old models.BambooTransformationRule
+       err := basicRes.GetDal().First(&old, dal.Where("id = ?", 
transformationRuleId))
+       if err != nil {
+               return nil, errors.Default.Wrap(err, "error on saving 
TransformationRule")
+       }
+       err = api.DecodeMapStruct(input.Body, &old)
+       if err != nil {
+               return nil, errors.Default.Wrap(err, "error decoding map into 
transformationRule")
+       }
+       old.ID = transformationRuleId
+       err = basicRes.GetDal().Update(&old, dal.Where("id = ?", 
transformationRuleId))
+       if err != nil {
+               if basicRes.GetDal().IsDuplicationError(err) {
+                       return nil, errors.BadInput.New("there was a 
transformation rule with the same name, please choose another name")
+               }
+               return nil, errors.BadInput.Wrap(err, "error on saving 
TransformationRule")
+       }
+       return &plugin.ApiResourceOutput{Body: old, Status: http.StatusOK}, nil
+}
+
+// GetTransformationRule return one transformation rule
+// @Summary return one transformation rule
+// @Description return one transformation rule
+// @Tags plugins/bamboo
+// @Param id path int true "id"
+// @Success 200  {object} models.BambooTransformationRule
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/bamboo/transformation_rules/{id} [GET]
+func GetTransformationRule(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       transformationRuleId, err := strconv.ParseUint(input.Params["id"], 10, 
64)
+       if err != nil {
+               return nil, errors.Default.Wrap(err, "the transformation rule 
ID should be an integer")
+       }
+       var rule models.BambooTransformationRule
+       err = basicRes.GetDal().First(&rule, dal.Where("id = ?", 
transformationRuleId))
+       if err != nil {
+               return nil, errors.Default.Wrap(err, "error on get 
TransformationRule")
+       }
+       return &plugin.ApiResourceOutput{Body: rule, Status: http.StatusOK}, nil
+}
+
+// GetTransformationRuleList return all transformation rules
+// @Summary return all transformation rules
+// @Description return all transformation rules
+// @Tags plugins/bamboo
+// @Param pageSize query int false "page size, default 50"
+// @Param page query int false "page size, default 1"
+// @Success 200  {object} []models.BambooTransformationRule
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/bamboo/transformation_rules [GET]
+func GetTransformationRuleList(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       var rules []models.BambooTransformationRule
+       limit, offset := api.GetLimitOffset(input.Query, "pageSize", "page")
+       err := basicRes.GetDal().All(&rules, dal.Limit(limit), 
dal.Offset(offset))
+       if err != nil {
+               return nil, errors.Default.Wrap(err, "error on get 
TransformationRule list")
+       }
+       return &plugin.ApiResourceOutput{Body: rules, Status: http.StatusOK}, 
nil
+}
diff --git a/backend/plugins/bamboo/bamboo.go b/backend/plugins/bamboo/bamboo.go
index bab746a2b..5d8f30e22 100644
--- a/backend/plugins/bamboo/bamboo.go
+++ b/backend/plugins/bamboo/bamboo.go
@@ -31,7 +31,7 @@ func main() {
        bambooCmd := &cobra.Command{Use: "bamboo"}
        connectionId := bambooCmd.Flags().Uint64P("Connection-id", "c", 0, 
"bamboo connection id")
        projectKey := bambooCmd.Flags().StringP("project-key", "p", "", "bamboo 
project key")
-       _ = bambooCmd.MarkFlagRequired("project-id")
+       _ = bambooCmd.MarkFlagRequired("project-key")
        bambooCmd.Run = func(cmd *cobra.Command, args []string) {
                runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
                        "connectionId": *connectionId,
diff --git a/backend/plugins/bamboo/impl/impl.go 
b/backend/plugins/bamboo/impl/impl.go
index 70cb8ede7..0c7588b2c 100644
--- a/backend/plugins/bamboo/impl/impl.go
+++ b/backend/plugins/bamboo/impl/impl.go
@@ -157,7 +157,9 @@ func (p Bamboo) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
                }
                op.BambooTransformationRule = &transformationRule
        }
-
+       if op.BambooTransformationRule == nil && op.TransformationRuleId == 0 {
+               op.BambooTransformationRule = 
new(models.BambooTransformationRule)
+       }
        return &tasks.BambooTaskData{
                Options:   op,
                ApiClient: apiClient,
@@ -187,6 +189,14 @@ func (p Bamboo) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler {
                        "PATCH":  api.PatchConnection,
                        "DELETE": api.DeleteConnection,
                },
+               "transformation_rules": {
+                       "POST": api.CreateTransformationRule,
+                       "GET":  api.GetTransformationRuleList,
+               },
+               "transformation_rules/:id": {
+                       "PATCH": api.UpdateTransformationRule,
+                       "GET":   api.GetTransformationRule,
+               },
                "connections/:connectionId/scopes": {
                        "GET": api.GetScopeList,
                        "PUT": api.PutScope,
diff --git a/backend/plugins/bamboo/models/connection.go 
b/backend/plugins/bamboo/models/connection.go
index 3f66b5df9..c5bdb48ad 100644
--- a/backend/plugins/bamboo/models/connection.go
+++ b/backend/plugins/bamboo/models/connection.go
@@ -72,11 +72,11 @@ type BambooResponse struct {
 }
 
 type ApiBambooServerInfo struct {
-       Version     string    `json:"version"`
-       Edition     string    `json:"edition"`
-       BuildDate   time.Time `json:"buildDate"`
-       BuildNumber string    `json:"buildNumber"`
-       State       string    `json:"state"`
+       Version     string     `json:"version"`
+       Edition     string     `json:"edition"`
+       BuildDate   *time.Time `json:"buildDate"`
+       BuildNumber string     `json:"buildNumber"`
+       State       string     `json:"state"`
 }
 
 type ApiRepository struct {
diff --git a/backend/plugins/bamboo/models/job_build.go 
b/backend/plugins/bamboo/models/job_build.go
index df4081042..6e06b5815 100644
--- a/backend/plugins/bamboo/models/job_build.go
+++ b/backend/plugins/bamboo/models/job_build.go
@@ -23,44 +23,45 @@ import (
 )
 
 type BambooJobBuild struct {
-       ConnectionId             uint64    `gorm:"primaryKey"`
-       JobBuildKey              string    `gorm:"primaryKey"`
-       Expand                   string    `json:"expand"`
-       Number                   int       `json:"number"`
-       BuildNumber              int       `json:"buildNumber"`
-       JobName                  string    `gorm:"index"`
-       JobKey                   string    `gorm:"index"`
-       PlanName                 string    `gorm:"index"`
-       PlanKey                  string    `gorm:"index"`
-       ProjectName              string    `gorm:"index"`
-       ProjectKey               string    `gorm:"index"`
-       BuildResultKey           string    `json:"buildResultKey"`
-       LifeCycleState           string    `json:"lifeCycleState"`
-       BuildStartedTime         time.Time `json:"buildStartedTime"`
-       PrettyBuildStartedTime   string    `json:"prettyBuildStartedTime"`
-       BuildCompletedTime       time.Time `json:"buildCompletedTime"`
-       BuildCompletedDate       time.Time `json:"buildCompletedDate"`
-       PrettyBuildCompletedTime string    `json:"prettyBuildCompletedTime"`
-       BuildDurationInSeconds   int       `json:"buildDurationInSeconds"`
-       BuildDuration            int       `json:"buildDuration"`
-       BuildDurationDescription string    `json:"buildDurationDescription"`
-       BuildRelativeTime        string    `json:"buildRelativeTime"`
-       VcsRevisionKey           string    `json:"vcsRevisionKey"`
-       BuildTestSummary         string    `json:"buildTestSummary"`
-       SuccessfulTestCount      int       `json:"successfulTestCount"`
-       FailedTestCount          int       `json:"failedTestCount"`
-       QuarantinedTestCount     int       `json:"quarantinedTestCount"`
-       SkippedTestCount         int       `json:"skippedTestCount"`
-       Continuable              bool      `json:"continuable"`
-       OnceOff                  bool      `json:"onceOff"`
-       Restartable              bool      `json:"restartable"`
-       NotRunYet                bool      `json:"notRunYet"`
-       Finished                 bool      `json:"finished"`
-       Successful               bool      `json:"successful"`
-       BuildReason              string    `json:"buildReason"`
-       ReasonSummary            string    `json:"reasonSummary"`
-       State                    string    `json:"state"`
-       BuildState               string    `json:"buildState"`
+       ConnectionId             uint64     `gorm:"primaryKey"`
+       JobBuildKey              string     `gorm:"primaryKey"`
+       PlanBuildKey             string     `gorm:"index"`
+       Expand                   string     `json:"expand"`
+       Number                   int        `json:"number"`
+       BuildNumber              int        `json:"buildNumber"`
+       JobName                  string     `gorm:"index"`
+       JobKey                   string     `gorm:"index"`
+       PlanName                 string     `gorm:"index"`
+       PlanKey                  string     `gorm:"index"`
+       ProjectName              string     `gorm:"index"`
+       ProjectKey               string     `gorm:"index"`
+       BuildResultKey           string     `json:"buildResultKey"`
+       LifeCycleState           string     `json:"lifeCycleState"`
+       BuildStartedTime         *time.Time `json:"buildStartedTime"`
+       PrettyBuildStartedTime   string     `json:"prettyBuildStartedTime"`
+       BuildCompletedTime       *time.Time `json:"buildCompletedTime"`
+       BuildCompletedDate       *time.Time `json:"buildCompletedDate"`
+       PrettyBuildCompletedTime string     `json:"prettyBuildCompletedTime"`
+       BuildDurationInSeconds   int        `json:"buildDurationInSeconds"`
+       BuildDuration            int        `json:"buildDuration"`
+       BuildDurationDescription string     `json:"buildDurationDescription"`
+       BuildRelativeTime        string     `json:"buildRelativeTime"`
+       VcsRevisionKey           string     `json:"vcsRevisionKey"`
+       BuildTestSummary         string     `json:"buildTestSummary"`
+       SuccessfulTestCount      int        `json:"successfulTestCount"`
+       FailedTestCount          int        `json:"failedTestCount"`
+       QuarantinedTestCount     int        `json:"quarantinedTestCount"`
+       SkippedTestCount         int        `json:"skippedTestCount"`
+       Continuable              bool       `json:"continuable"`
+       OnceOff                  bool       `json:"onceOff"`
+       Restartable              bool       `json:"restartable"`
+       NotRunYet                bool       `json:"notRunYet"`
+       Finished                 bool       `json:"finished"`
+       Successful               bool       `json:"successful"`
+       BuildReason              string     `json:"buildReason"`
+       ReasonSummary            string     `json:"reasonSummary"`
+       State                    string     `json:"state"`
+       BuildState               string     `json:"buildState"`
        JobResultKey             string
        common.NoPKModel
 }
diff --git 
a/backend/plugins/bamboo/models/migrationscripts/20230216_add_init_tables.go 
b/backend/plugins/bamboo/models/migrationscripts/20230216_add_init_tables.go
index bfb399c34..0845e1c09 100644
--- a/backend/plugins/bamboo/models/migrationscripts/20230216_add_init_tables.go
+++ b/backend/plugins/bamboo/models/migrationscripts/20230216_add_init_tables.go
@@ -34,6 +34,7 @@ func (u *addInitTables) Up(baseRes context.BasicRes) 
errors.Error {
                &archived.BambooPlanBuild{},
                &archived.BambooPlanBuildVcsRevision{},
                &archived.BambooJobBuild{},
+               &archived.BambooTransformationRule{},
        )
        return migrationhelper.AutoMigrateTables(
                baseRes,
@@ -44,11 +45,12 @@ func (u *addInitTables) Up(baseRes context.BasicRes) 
errors.Error {
                &archived.BambooPlanBuild{},
                &archived.BambooPlanBuildVcsRevision{},
                &archived.BambooJobBuild{},
+               &archived.BambooTransformationRule{},
        )
 }
 
 func (*addInitTables) Version() uint64 {
-       return 20230221205032
+       return 20230221205033
 }
 
 func (*addInitTables) Name() string {
diff --git 
a/backend/plugins/bamboo/models/migrationscripts/archived/connection.go 
b/backend/plugins/bamboo/models/migrationscripts/archived/connection.go
index e7e3d1ade..eb9e250bf 100644
--- a/backend/plugins/bamboo/models/migrationscripts/archived/connection.go
+++ b/backend/plugins/bamboo/models/migrationscripts/archived/connection.go
@@ -20,9 +20,9 @@ package archived
 import "time"
 
 type Model struct {
-       ID        uint64    `gorm:"primaryKey" json:"id"`
-       CreatedAt time.Time `json:"createdAt"`
-       UpdatedAt time.Time `json:"updatedAt"`
+       ID        uint64     `gorm:"primaryKey" json:"id"`
+       CreatedAt *time.Time `json:"createdAt"`
+       UpdatedAt *time.Time `json:"updatedAt"`
 }
 type BaseConnection struct {
        Name string `gorm:"type:varchar(100);uniqueIndex" json:"name" 
validate:"required"`
diff --git 
a/backend/plugins/bamboo/models/migrationscripts/archived/job_build.go 
b/backend/plugins/bamboo/models/migrationscripts/archived/job_build.go
index 9991e62d7..37a25295e 100644
--- a/backend/plugins/bamboo/models/migrationscripts/archived/job_build.go
+++ b/backend/plugins/bamboo/models/migrationscripts/archived/job_build.go
@@ -23,44 +23,45 @@ import (
 )
 
 type BambooJobBuild struct {
-       ConnectionId             uint64    `gorm:"primaryKey"`
-       JobBuildKey              string    `gorm:"primaryKey"`
-       Expand                   string    `json:"expand"`
-       Number                   int       `json:"number"`
-       BuildNumber              int       `json:"buildNumber"`
-       JobName                  string    `gorm:"index"`
-       JobKey                   string    `gorm:"index"`
-       PlanName                 string    `gorm:"index"`
-       PlanKey                  string    `gorm:"index"`
-       ProjectName              string    `gorm:"index"`
-       ProjectKey               string    `gorm:"index"`
-       BuildResultKey           string    `json:"buildResultKey"`
-       LifeCycleState           string    `json:"lifeCycleState"`
-       BuildStartedTime         time.Time `json:"buildStartedTime"`
-       PrettyBuildStartedTime   string    `json:"prettyBuildStartedTime"`
-       BuildCompletedTime       time.Time `json:"buildCompletedTime"`
-       BuildCompletedDate       time.Time `json:"buildCompletedDate"`
-       PrettyBuildCompletedTime string    `json:"prettyBuildCompletedTime"`
-       BuildDurationInSeconds   int       `json:"buildDurationInSeconds"`
-       BuildDuration            int       `json:"buildDuration"`
-       BuildDurationDescription string    `json:"buildDurationDescription"`
-       BuildRelativeTime        string    `json:"buildRelativeTime"`
-       VcsRevisionKey           string    `json:"vcsRevisionKey"`
-       BuildTestSummary         string    `json:"buildTestSummary"`
-       SuccessfulTestCount      int       `json:"successfulTestCount"`
-       FailedTestCount          int       `json:"failedTestCount"`
-       QuarantinedTestCount     int       `json:"quarantinedTestCount"`
-       SkippedTestCount         int       `json:"skippedTestCount"`
-       Continuable              bool      `json:"continuable"`
-       OnceOff                  bool      `json:"onceOff"`
-       Restartable              bool      `json:"restartable"`
-       NotRunYet                bool      `json:"notRunYet"`
-       Finished                 bool      `json:"finished"`
-       Successful               bool      `json:"successful"`
-       BuildReason              string    `json:"buildReason"`
-       ReasonSummary            string    `json:"reasonSummary"`
-       State                    string    `json:"state"`
-       BuildState               string    `json:"buildState"`
+       ConnectionId             uint64     `gorm:"primaryKey"`
+       JobBuildKey              string     `gorm:"primaryKey"`
+       PlanBuildKey             string     `gorm:"index"`
+       Expand                   string     `json:"expand"`
+       Number                   int        `json:"number"`
+       BuildNumber              int        `json:"buildNumber"`
+       JobName                  string     `gorm:"index"`
+       JobKey                   string     `gorm:"index"`
+       PlanName                 string     `gorm:"index"`
+       PlanKey                  string     `gorm:"index"`
+       ProjectName              string     `gorm:"index"`
+       ProjectKey               string     `gorm:"index"`
+       BuildResultKey           string     `json:"buildResultKey"`
+       LifeCycleState           string     `json:"lifeCycleState"`
+       BuildStartedTime         *time.Time `json:"buildStartedTime"`
+       PrettyBuildStartedTime   string     `json:"prettyBuildStartedTime"`
+       BuildCompletedTime       *time.Time `json:"buildCompletedTime"`
+       BuildCompletedDate       *time.Time `json:"buildCompletedDate"`
+       PrettyBuildCompletedTime string     `json:"prettyBuildCompletedTime"`
+       BuildDurationInSeconds   int        `json:"buildDurationInSeconds"`
+       BuildDuration            int        `json:"buildDuration"`
+       BuildDurationDescription string     `json:"buildDurationDescription"`
+       BuildRelativeTime        string     `json:"buildRelativeTime"`
+       VcsRevisionKey           string     `json:"vcsRevisionKey"`
+       BuildTestSummary         string     `json:"buildTestSummary"`
+       SuccessfulTestCount      int        `json:"successfulTestCount"`
+       FailedTestCount          int        `json:"failedTestCount"`
+       QuarantinedTestCount     int        `json:"quarantinedTestCount"`
+       SkippedTestCount         int        `json:"skippedTestCount"`
+       Continuable              bool       `json:"continuable"`
+       OnceOff                  bool       `json:"onceOff"`
+       Restartable              bool       `json:"restartable"`
+       NotRunYet                bool       `json:"notRunYet"`
+       Finished                 bool       `json:"finished"`
+       Successful               bool       `json:"successful"`
+       BuildReason              string     `json:"buildReason"`
+       ReasonSummary            string     `json:"reasonSummary"`
+       State                    string     `json:"state"`
+       BuildState               string     `json:"buildState"`
        JobResultKey             string
        archived.NoPKModel
 }
diff --git 
a/backend/plugins/bamboo/models/migrationscripts/archived/plan_build.go 
b/backend/plugins/bamboo/models/migrationscripts/archived/plan_build.go
index e9391ced5..e4b293326 100644
--- a/backend/plugins/bamboo/models/migrationscripts/archived/plan_build.go
+++ b/backend/plugins/bamboo/models/migrationscripts/archived/plan_build.go
@@ -32,33 +32,33 @@ type BambooPlanBuild struct {
        PlanKey                  string
        ProjectName              string `json:"projectName"`
        ProjectKey               string
-       BuildResultKey           string    `json:"buildResultKey"`
-       LifeCycleState           string    `json:"lifeCycleState"`
-       BuildStartedTime         time.Time `json:"buildStartedTime"`
-       PrettyBuildStartedTime   string    `json:"prettyBuildStartedTime"`
-       BuildCompletedTime       time.Time `json:"buildCompletedTime"`
-       BuildCompletedDate       time.Time `json:"buildCompletedDate"`
-       PrettyBuildCompletedTime string    `json:"prettyBuildCompletedTime"`
-       BuildDurationInSeconds   int       `json:"buildDurationInSeconds"`
-       BuildDuration            int       `json:"buildDuration"`
-       BuildDurationDescription string    `json:"buildDurationDescription"`
-       BuildRelativeTime        string    `json:"buildRelativeTime"`
-       VcsRevisionKey           string    `json:"vcsRevisionKey"`
-       BuildTestSummary         string    `json:"buildTestSummary"`
-       SuccessfulTestCount      int       `json:"successfulTestCount"`
-       FailedTestCount          int       `json:"failedTestCount"`
-       QuarantinedTestCount     int       `json:"quarantinedTestCount"`
-       SkippedTestCount         int       `json:"skippedTestCount"`
-       Continuable              bool      `json:"continuable"`
-       OnceOff                  bool      `json:"onceOff"`
-       Restartable              bool      `json:"restartable"`
-       NotRunYet                bool      `json:"notRunYet"`
-       Finished                 bool      `json:"finished"`
-       Successful               bool      `json:"successful"`
-       BuildReason              string    `json:"buildReason"`
-       ReasonSummary            string    `json:"reasonSummary"`
-       State                    string    `json:"state"`
-       BuildState               string    `json:"buildState"`
+       BuildResultKey           string     `json:"buildResultKey"`
+       LifeCycleState           string     `json:"lifeCycleState"`
+       BuildStartedTime         *time.Time `json:"buildStartedTime"`
+       PrettyBuildStartedTime   string     `json:"prettyBuildStartedTime"`
+       BuildCompletedTime       *time.Time `json:"buildCompletedTime"`
+       BuildCompletedDate       *time.Time `json:"buildCompletedDate"`
+       PrettyBuildCompletedTime string     `json:"prettyBuildCompletedTime"`
+       BuildDurationInSeconds   int        `json:"buildDurationInSeconds"`
+       BuildDuration            int        `json:"buildDuration"`
+       BuildDurationDescription string     `json:"buildDurationDescription"`
+       BuildRelativeTime        string     `json:"buildRelativeTime"`
+       VcsRevisionKey           string     `json:"vcsRevisionKey"`
+       BuildTestSummary         string     `json:"buildTestSummary"`
+       SuccessfulTestCount      int        `json:"successfulTestCount"`
+       FailedTestCount          int        `json:"failedTestCount"`
+       QuarantinedTestCount     int        `json:"quarantinedTestCount"`
+       SkippedTestCount         int        `json:"skippedTestCount"`
+       Continuable              bool       `json:"continuable"`
+       OnceOff                  bool       `json:"onceOff"`
+       Restartable              bool       `json:"restartable"`
+       NotRunYet                bool       `json:"notRunYet"`
+       Finished                 bool       `json:"finished"`
+       Successful               bool       `json:"successful"`
+       BuildReason              string     `json:"buildReason"`
+       ReasonSummary            string     `json:"reasonSummary"`
+       State                    string     `json:"state"`
+       BuildState               string     `json:"buildState"`
        PlanResultKey            string
        archived.NoPKModel
 }
diff --git a/backend/plugins/bamboo/models/migrationscripts/archived/project.go 
b/backend/plugins/bamboo/models/migrationscripts/archived/project.go
index ccabf8ef1..c1d8f482a 100644
--- a/backend/plugins/bamboo/models/migrationscripts/archived/project.go
+++ b/backend/plugins/bamboo/models/migrationscripts/archived/project.go
@@ -22,14 +22,15 @@ import (
 )
 
 type BambooProject struct {
-       ConnectionId         uint64 `json:"connectionId" 
mapstructure:"connectionId" gorm:"primaryKey"`
-       ProjectKey           string `json:"projectKey" 
gorm:"primaryKey;type:varchar(256)"`
-       TransformationRuleId uint64 `json:"transformationRuleId,omitempty" 
mapstructure:"transformationRuleId"`
-       Name                 string `json:"name" gorm:"index;type:varchar(256)"`
+       ConnectionId         uint64 `gorm:"primaryKey"`
+       ProjectKey           string `gorm:"primaryKey;type:varchar(100)"`
+       Expand               string `json:"expand"`
+       Name                 string `gorm:"index;type:varchar(100)"`
        Description          string `json:"description"`
        Href                 string `json:"link"`
-       Rel                  string `json:"rel" gorm:"type:varchar(100)"`
-       archived.NoPKModel   `json:"-" mapstructure:"-"`
+       Rel                  string `gorm:"type:varchar(100)"`
+       TransformationRuleId uint64 `json:"transformationRuleId,omitempty" 
mapstructure:"transformationRuleId"`
+       archived.NoPKModel
 }
 
 func (BambooProject) TableName() string {
diff --git a/backend/plugins/bamboo/models/transformation_rule.go 
b/backend/plugins/bamboo/models/migrationscripts/archived/transformation_rule.go
similarity index 65%
copy from backend/plugins/bamboo/models/transformation_rule.go
copy to 
backend/plugins/bamboo/models/migrationscripts/archived/transformation_rule.go
index 4d48d5f19..18354eb8c 100644
--- a/backend/plugins/bamboo/models/transformation_rule.go
+++ 
b/backend/plugins/bamboo/models/migrationscripts/archived/transformation_rule.go
@@ -15,17 +15,22 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package models
+package archived
 
 import (
-       "github.com/apache/incubator-devlake/core/models/common"
+       
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+       "gorm.io/datatypes"
 )
 
 type BambooTransformationRule struct {
-       common.Model
+       archived.Model
        Name string `gorm:"type:varchar(255);index:idx_name_gitlab,unique" 
validate:"required" mapstructure:"name" json:"name"`
+       // should be {realRepoName: [bamboo_repoId]}
+       RepoMap           datatypes.JSONMap
+       DeploymentPattern string `mapstructure:"deploymentPattern,omitempty" 
json:"deploymentPattern" gorm:"type:varchar(255)"`
+       ProductionPattern string `mapstructure:"productionPattern,omitempty" 
json:"productionPattern" gorm:"type:varchar(255)"`
 }
 
-func (t BambooTransformationRule) TableName() string {
+func (BambooTransformationRule) TableName() string {
        return "_tool_bamboo_transformation_rules"
 }
diff --git a/backend/plugins/bamboo/models/plan_build.go 
b/backend/plugins/bamboo/models/plan_build.go
index 07588e1a5..48845725b 100644
--- a/backend/plugins/bamboo/models/plan_build.go
+++ b/backend/plugins/bamboo/models/plan_build.go
@@ -32,33 +32,33 @@ type BambooPlanBuild struct {
        PlanKey                  string
        ProjectName              string `json:"projectName"`
        ProjectKey               string
-       BuildResultKey           string    `json:"buildResultKey"`
-       LifeCycleState           string    `json:"lifeCycleState"`
-       BuildStartedTime         time.Time `json:"buildStartedTime"`
-       PrettyBuildStartedTime   string    `json:"prettyBuildStartedTime"`
-       BuildCompletedTime       time.Time `json:"buildCompletedTime"`
-       BuildCompletedDate       time.Time `json:"buildCompletedDate"`
-       PrettyBuildCompletedTime string    `json:"prettyBuildCompletedTime"`
-       BuildDurationInSeconds   int       `json:"buildDurationInSeconds"`
-       BuildDuration            int       `json:"buildDuration"`
-       BuildDurationDescription string    `json:"buildDurationDescription"`
-       BuildRelativeTime        string    `json:"buildRelativeTime"`
-       VcsRevisionKey           string    `json:"vcsRevisionKey"`
-       BuildTestSummary         string    `json:"buildTestSummary"`
-       SuccessfulTestCount      int       `json:"successfulTestCount"`
-       FailedTestCount          int       `json:"failedTestCount"`
-       QuarantinedTestCount     int       `json:"quarantinedTestCount"`
-       SkippedTestCount         int       `json:"skippedTestCount"`
-       Continuable              bool      `json:"continuable"`
-       OnceOff                  bool      `json:"onceOff"`
-       Restartable              bool      `json:"restartable"`
-       NotRunYet                bool      `json:"notRunYet"`
-       Finished                 bool      `json:"finished"`
-       Successful               bool      `json:"successful"`
-       BuildReason              string    `json:"buildReason"`
-       ReasonSummary            string    `json:"reasonSummary"`
-       State                    string    `json:"state"`
-       BuildState               string    `json:"buildState"`
+       BuildResultKey           string     `json:"buildResultKey"`
+       LifeCycleState           string     `json:"lifeCycleState"`
+       BuildStartedTime         *time.Time `json:"buildStartedTime"`
+       PrettyBuildStartedTime   string     `json:"prettyBuildStartedTime"`
+       BuildCompletedTime       *time.Time `json:"buildCompletedTime"`
+       BuildCompletedDate       *time.Time `json:"buildCompletedDate"`
+       PrettyBuildCompletedTime string     `json:"prettyBuildCompletedTime"`
+       BuildDurationInSeconds   int        `json:"buildDurationInSeconds"`
+       BuildDuration            int        `json:"buildDuration"`
+       BuildDurationDescription string     `json:"buildDurationDescription"`
+       BuildRelativeTime        string     `json:"buildRelativeTime"`
+       VcsRevisionKey           string     `json:"vcsRevisionKey"`
+       BuildTestSummary         string     `json:"buildTestSummary"`
+       SuccessfulTestCount      int        `json:"successfulTestCount"`
+       FailedTestCount          int        `json:"failedTestCount"`
+       QuarantinedTestCount     int        `json:"quarantinedTestCount"`
+       SkippedTestCount         int        `json:"skippedTestCount"`
+       Continuable              bool       `json:"continuable"`
+       OnceOff                  bool       `json:"onceOff"`
+       Restartable              bool       `json:"restartable"`
+       NotRunYet                bool       `json:"notRunYet"`
+       Finished                 bool       `json:"finished"`
+       Successful               bool       `json:"successful"`
+       BuildReason              string     `json:"buildReason"`
+       ReasonSummary            string     `json:"reasonSummary"`
+       State                    string     `json:"state"`
+       BuildState               string     `json:"buildState"`
        PlanResultKey            string
        common.NoPKModel
 }
@@ -108,22 +108,22 @@ func (BambooPlanBuild) Convert(apiRes 
*ApiBambooPlanBuild) *BambooPlanBuild {
 }
 
 type ApiBambooPlanBuild struct {
-       Expand                   string    `json:"expand"`
-       PlanName                 string    `json:"planName"`
-       ProjectName              string    `json:"projectName"`
-       BuildResultKey           string    `json:"buildResultKey"`
-       LifeCycleState           string    `json:"lifeCycleState"`
-       Id                       int       `json:"id"`
-       BuildStartedTime         time.Time `json:"buildStartedTime"`
-       PrettyBuildStartedTime   string    `json:"prettyBuildStartedTime"`
-       BuildCompletedTime       time.Time `json:"buildCompletedTime"`
-       BuildCompletedDate       time.Time `json:"buildCompletedDate"`
-       PrettyBuildCompletedTime string    `json:"prettyBuildCompletedTime"`
-       BuildDurationInSeconds   int       `json:"buildDurationInSeconds"`
-       BuildDuration            int       `json:"buildDuration"`
-       BuildDurationDescription string    `json:"buildDurationDescription"`
-       BuildRelativeTime        string    `json:"buildRelativeTime"`
-       VcsRevisionKey           string    `json:"vcsRevisionKey"`
+       Expand                   string     `json:"expand"`
+       PlanName                 string     `json:"planName"`
+       ProjectName              string     `json:"projectName"`
+       BuildResultKey           string     `json:"buildResultKey"`
+       LifeCycleState           string     `json:"lifeCycleState"`
+       Id                       int        `json:"id"`
+       BuildStartedTime         *time.Time `json:"buildStartedTime"`
+       PrettyBuildStartedTime   string     `json:"prettyBuildStartedTime"`
+       BuildCompletedTime       *time.Time `json:"buildCompletedTime"`
+       BuildCompletedDate       *time.Time `json:"buildCompletedDate"`
+       PrettyBuildCompletedTime string     `json:"prettyBuildCompletedTime"`
+       BuildDurationInSeconds   int        `json:"buildDurationInSeconds"`
+       BuildDuration            int        `json:"buildDuration"`
+       BuildDurationDescription string     `json:"buildDurationDescription"`
+       BuildRelativeTime        string     `json:"buildRelativeTime"`
+       VcsRevisionKey           string     `json:"vcsRevisionKey"`
        VcsRevisions             struct {
                Size        int    `json:"size"`
                Expand      string `json:"expand"`
@@ -160,4 +160,26 @@ type ApiBambooPlanBuild struct {
        BuildState  string `json:"buildState"`
        Number      int    `json:"number"`
        BuildNumber int    `json:"buildNumber"`
+       Parent      `json:"parent"`
 }
+
+type Parent struct {
+       Href string `json:"href"`
+       Rel  string `json:"rel"`
+}
+
+const (
+       FAILED      = "Failed"
+       ERROR       = "ERROR"
+       UNDEPLOYED  = "UNDEPLOYED"
+       UNKNOWN     = "Unknown"
+       STOPPED     = "Stopped"
+       SKIPPED     = "Skipped"
+       SUCCESSFUL  = "Successful"
+       COMPLETED   = "COMPLETED"
+       PAUSED      = "COMPLETED"
+       HALTED      = "HALTED"
+       IN_PROGRESS = "IN_PROGRESS"
+       PENDING     = "PENDING"
+       BUILDING    = "BUILDING"
+)
diff --git a/backend/plugins/bamboo/models/transformation_rule.go 
b/backend/plugins/bamboo/models/transformation_rule.go
index 4d48d5f19..2a6bca0cd 100644
--- a/backend/plugins/bamboo/models/transformation_rule.go
+++ b/backend/plugins/bamboo/models/transformation_rule.go
@@ -19,13 +19,18 @@ package models
 
 import (
        "github.com/apache/incubator-devlake/core/models/common"
+       "gorm.io/datatypes"
 )
 
 type BambooTransformationRule struct {
        common.Model
        Name string `gorm:"type:varchar(255);index:idx_name_gitlab,unique" 
validate:"required" mapstructure:"name" json:"name"`
+       // should be {realRepoName: [bamboo_repoId]}
+       RepoMap           datatypes.JSONMap
+       DeploymentPattern string `mapstructure:"deploymentPattern,omitempty" 
json:"deploymentPattern" gorm:"type:varchar(255)"`
+       ProductionPattern string `mapstructure:"productionPattern,omitempty" 
json:"productionPattern" gorm:"type:varchar(255)"`
 }
 
-func (t BambooTransformationRule) TableName() string {
+func (BambooTransformationRule) TableName() string {
        return "_tool_bamboo_transformation_rules"
 }
diff --git a/backend/plugins/bamboo/tasks/job_build_convertor.go 
b/backend/plugins/bamboo/tasks/job_build_convertor.go
new file mode 100644
index 000000000..e82ff35d8
--- /dev/null
+++ b/backend/plugins/bamboo/tasks/job_build_convertor.go
@@ -0,0 +1,102 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "reflect"
+
+       "github.com/apache/incubator-devlake/core/models/domainlayer/devops"
+
+       "github.com/apache/incubator-devlake/core/dal"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+       "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/bamboo/models"
+)
+
+var ConvertJobBuildsMeta = plugin.SubTaskMeta{
+       Name:             "convertJobBuilds",
+       EntryPoint:       ConvertJobBuilds,
+       EnabledByDefault: true,
+       Description:      "Convert tool layer table bamboo_jobBuilds into  
domain layer table jobBuilds",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+}
+
+func ConvertJobBuilds(taskCtx plugin.SubTaskContext) errors.Error {
+       db := taskCtx.GetDal()
+       rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, 
RAW_JOB_BUILD_TABLE)
+       deploymentPattern := data.Options.DeploymentPattern
+       productionPattern := data.Options.ProductionPattern
+       regexEnricher := api.NewRegexEnricher()
+       err := regexEnricher.AddRegexp(deploymentPattern, productionPattern)
+       if err != nil {
+               return err
+       }
+       cursor, err := db.Cursor(
+               dal.From(&models.BambooJobBuild{}),
+               dal.Where("connection_id = ? and project_key = ?", 
data.Options.ConnectionId, data.Options.ProjectKey))
+       if err != nil {
+               return err
+       }
+       defer cursor.Close()
+
+       jobBuildIdGen := didgen.NewDomainIdGenerator(&models.BambooJobBuild{})
+       planBuildIdGen := didgen.NewDomainIdGenerator(&models.BambooPlanBuild{})
+       projectIdGen := didgen.NewDomainIdGenerator(&models.BambooProject{})
+
+       converter, err := api.NewDataConverter(api.DataConverterArgs{
+               InputRowType:       reflect.TypeOf(models.BambooJobBuild{}),
+               Input:              cursor,
+               RawDataSubTaskArgs: *rawDataSubTaskArgs,
+               Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
+                       line := inputRow.(*models.BambooJobBuild)
+                       domainJobBuild := &devops.CICDTask{
+                               DomainEntity: domainlayer.DomainEntity{Id: 
jobBuildIdGen.Generate(data.Options.ConnectionId, line.JobBuildKey)},
+                               Name:         line.JobName,
+                               StartedDate:  *line.BuildStartedTime,
+                               FinishedDate: line.BuildCompletedDate,
+                               PipelineId:   
planBuildIdGen.Generate(data.Options.ConnectionId, line.PlanBuildKey),
+                               CicdScopeId:  
projectIdGen.Generate(data.Options.ConnectionId, line.ProjectKey),
+                       }
+                       if !line.Finished {
+                               domainJobBuild.Status = devops.IN_PROGRESS
+                       } else {
+                               domainJobBuild.Status = devops.DONE
+                       }
+                       if !line.Successful {
+                               domainJobBuild.Result = devops.FAILURE
+                       } else {
+                               domainJobBuild.Result = devops.SUCCESS
+                       }
+                       domainJobBuild.Type = 
regexEnricher.GetEnrichResult(deploymentPattern, line.JobName, 
devops.DEPLOYMENT)
+                       domainJobBuild.Environment = 
regexEnricher.GetEnrichResult(productionPattern, line.JobName, 
devops.PRODUCTION)
+
+                       return []interface{}{
+                               domainJobBuild,
+                       }, nil
+               },
+       })
+
+       if err != nil {
+               return err
+       }
+
+       return converter.Execute()
+}
diff --git a/backend/plugins/bamboo/tasks/job_build_extractor.go 
b/backend/plugins/bamboo/tasks/job_build_extractor.go
index b7ff68e21..6d950ce6f 100644
--- a/backend/plugins/bamboo/tasks/job_build_extractor.go
+++ b/backend/plugins/bamboo/tasks/job_build_extractor.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
        "encoding/json"
+       "fmt"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
@@ -29,7 +30,7 @@ var _ plugin.SubTaskEntryPoint = ExtractJobBuild
 
 func ExtractJobBuild(taskCtx plugin.SubTaskContext) errors.Error {
        rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, 
RAW_JOB_BUILD_TABLE)
-
+       //repoMap := getRepoMap(data.Options.BambooTransformationRule.RepoMap)
        extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
                RawDataSubTaskArgs: *rawDataSubTaskArgs,
 
@@ -50,7 +51,7 @@ func ExtractJobBuild(taskCtx plugin.SubTaskContext) 
errors.Error {
                        body.JobKey = plan.JobKey
                        body.PlanKey = plan.PlanKey
                        body.PlanName = plan.PlanName
-
+                       body.PlanBuildKey = fmt.Sprintf("%s-%v", plan.PlanKey, 
body.Number)
                        results := make([]interface{}, 0)
                        results = append(results, body)
                        for _, v := range res.VcsRevisions.VcsRevision {
@@ -79,3 +80,16 @@ var ExtractJobBuildMeta = plugin.SubTaskMeta{
        Description:      "Extract raw data into tool layer table 
bamboo_plan_builds",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
 }
+
+// will be used in next pr
+//func getRepoMap(rawRepoMap datatypes.JSONMap) map[int]string {
+//     repoMap := make(map[int]string)
+//     for k, v := range rawRepoMap {
+//             if list, ok := v.([]int); ok {
+//                     for _, id := range list {
+//                             repoMap[id] = k
+//                     }
+//             }
+//     }
+//     return repoMap
+//}
diff --git a/backend/plugins/bamboo/tasks/plan_build_extractor.go 
b/backend/plugins/bamboo/tasks/plan_build_extractor.go
index 6c0b8d3ae..9fccf90a1 100644
--- a/backend/plugins/bamboo/tasks/plan_build_extractor.go
+++ b/backend/plugins/bamboo/tasks/plan_build_extractor.go
@@ -53,6 +53,7 @@ func ExtractPlanBuild(taskCtx plugin.SubTaskContext) 
errors.Error {
                        results = append(results, body)
                        // As job build can get more accuracy repo info,
                        // we can collect BambooPlanBuildVcsRevision in 
job_biuld_extractor
+                       // keep this because we might need it later
                        /*
                                for _, v := range res.VcsRevisions.VcsRevision {
                                        results = append(results, 
&models.BambooPlanBuildVcsRevision{


Reply via email to