You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by wa...@apache.org on 2023/02/21 14:11:22 UTC

[incubator-devlake] branch main updated: feat(bamboo): add job build convertor (#4470)

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 <yi...@merico.dev>
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{