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

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new a7217dbf2 feat(gitlab): add deployment entity (#6165)
a7217dbf2 is described below

commit a7217dbf29a1ec847b5e0275183be4c75ea4944d
Author: Lynwee <[email protected]>
AuthorDate: Sun Oct 8 11:09:20 2023 +0800

    feat(gitlab): add deployment entity (#6165)
    
    * feat(gitlab): add deployment entity
    
    * feat(gitlab): support transformation for deployment's environment field
    
    * fix(gitlab): move GitlabDeployment to  migration's archived package and 
add gitlab_id field
    
    * fix(gitlab): make DeployableDuration *float64 instead of float64
    
    * fix(gitlab): remove unused if statements
    
    * fix(gitlab): update DeployableDuration, make it *float64 in migration 
script
    
    * fix(gitlab): make deployment to support incremental update
    
    * fix(gitlab): remove unused codes
---
 backend/plugins/github/utils/utils.go              |   2 +-
 backend/plugins/gitlab/impl/impl.go                |   1 +
 backend/plugins/gitlab/models/deployment.go        |  98 +++++++++
 .../{register.go => 20230926_add_deployment.go}    |  37 ++--
 .../models/migrationscripts/archived/deployment.go |  98 +++++++++
 .../gitlab/models/migrationscripts/register.go     |   1 +
 .../plugins/gitlab/tasks/deployment_collector.go   |  75 +++++++
 .../plugins/gitlab/tasks/deployment_convertor.go   | 138 ++++++++++++
 .../plugins/gitlab/tasks/deployment_extractor.go   | 236 +++++++++++++++++++++
 9 files changed, 664 insertions(+), 22 deletions(-)

diff --git a/backend/plugins/github/utils/utils.go 
b/backend/plugins/github/utils/utils.go
index f2c1e7158..85125e602 100644
--- a/backend/plugins/github/utils/utils.go
+++ b/backend/plugins/github/utils/utils.go
@@ -75,7 +75,7 @@ func GetRateLimitPerSecond(info RateLimitInfo) int {
        unixResetTime := info.ResetTime.Unix()
        unixNow := info.Date.Unix()
        timeBetweenNowAndReset := unixResetTime - unixNow
-       // Adjust the remaining to be less then actual to avoid hitting the 
limit exactly.
+       // Adjust the remaining to be less than actual to avoid hitting the 
limit exactly.
        multiplier := 0.98
        adjustedRemaining := float64(info.Remaining) * multiplier
        return int(adjustedRemaining / float64(timeBetweenNowAndReset)) //* 
multiplier
diff --git a/backend/plugins/gitlab/impl/impl.go 
b/backend/plugins/gitlab/impl/impl.go
index 5b1f79dc5..d4799ab3e 100644
--- a/backend/plugins/gitlab/impl/impl.go
+++ b/backend/plugins/gitlab/impl/impl.go
@@ -101,6 +101,7 @@ func (p Gitlab) GetTablesInfo() []dal.Tabler {
                &models.GitlabTag{},
                &models.GitlabIssueAssignee{},
                &models.GitlabScopeConfig{},
+               &models.GitlabDeployment{},
        }
 }
 
diff --git a/backend/plugins/gitlab/models/deployment.go 
b/backend/plugins/gitlab/models/deployment.go
new file mode 100644
index 000000000..2eb353b4c
--- /dev/null
+++ b/backend/plugins/gitlab/models/deployment.go
@@ -0,0 +1,98 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package models
+
+import (
+       "github.com/apache/incubator-devlake/core/models/common"
+       "time"
+)
+
+type GitlabDeployment struct {
+       common.NoPKModel `swaggerignore:"true" json:"-" mapstructure:"-"`
+
+       ConnectionId uint64 `json:"connection_id" gorm:"primaryKey"`
+       GitlabId     int    `json:"gitlab_id" gorm:"primaryKey"`
+
+       CreatedDate time.Time `json:"created_date"`
+       UpdatedDate time.Time `json:"updated_date"`
+       Status      string    `json:"status"` //created, running, success, 
failed, canceled, or blocked
+
+       DeploymentId int    `json:"id" gorm:"primaryKey"`
+       Iid          int    `json:"iid"`
+       Ref          string `json:"ref"`
+       Sha          string `json:"sha"`
+       Environment  string `json:"environment" gorm:"type:varchar(255)"`
+       Name         string `json:"name" gorm:"type:varchar(255)"`
+
+       DeployableCommitAuthorEmail string    
`json:"deployable_commit_author_email" gorm:"type:varchar(255)"`
+       DeployableCommitAuthorName  string    
`json:"deployable_commit_author_name" gorm:"type:varchar(255)"`
+       DeployableCommitCreatedAt   time.Time 
`json:"deployable_commit_created_at"`
+       DeployableCommitID          string    `json:"deployable_commit_id" 
gorm:"type:varchar(255)"`
+       DeployableCommitMessage     string    `json:"deployable_commit_message" 
gorm:"type:varchar(255)"`
+       DeployableCommitShortID     string    
`json:"deployable_commit_short_id" gorm:"type:varchar(255)"`
+       DeployableCommitTitle       string    `json:"deployable_commit_title" 
gorm:"type:varchar(255)"`
+
+       //DeployableCoverage   any       `json:"deployable_coverage"`
+       DeployableCreatedAt  *time.Time `json:"deployable_created_at"`
+       DeployableFinishedAt *time.Time `json:"deployable_finished_at"`
+       DeployableID         int        `json:"deployable_id"`
+       DeployableName       string     `json:"deployable_name" 
gorm:"type:varchar(255)"`
+       DeployableRef        string     `json:"deployable_ref" 
gorm:"type:varchar(255)"`
+       //DeployableRunner     any       `json:"deployable_runner"`
+       DeployableStage     string     `json:"deployable_stage" 
gorm:"type:varchar(255)"`
+       DeployableStartedAt *time.Time `json:"deployable_started_at"`
+       DeployableStatus    string     `json:"deployable_status" 
gorm:"type:varchar(255)"`
+       DeployableTag       bool       `json:"deployable_tag"`
+       DeployableDuration  *float64   `json:"deployable_duration"`
+       QueuedDuration      float64    `json:"queued_duration"`
+
+       DeployableUserID        int       `json:"deployable_user_id"`
+       DeployableUserName      string    `json:"deployable_user_name" 
gorm:"type:varchar(255)"`
+       DeployableUserUsername  string    `json:"deployable_user_username" 
gorm:"type:varchar(255)"`
+       DeployableUserState     string    `json:"deployable_user_state" 
gorm:"type:varchar(255)"`
+       DeployableUserAvatarURL string    `json:"deployable_user_avatar_url" 
gorm:"type:varchar(255)"`
+       DeployableUserWebURL    string    `json:"deployable_user_web_url" 
gorm:"type:varchar(255)"`
+       DeployableUserCreatedAt time.Time `json:"deployable_user_created_at"`
+       //DeployableUserBio          any       `json:"deployable_user_bio"`
+       //DeployableUserLocation     any       `json:"deployable_user_location"`
+       DeployableUserPublicEmail  string `json:"deployable_user_public_email" 
gorm:"type:varchar(255)"`
+       DeployableUserSkype        string `json:"deployable_user_skype" 
gorm:"type:varchar(255)"`
+       DeployableUserLinkedin     string `json:"deployable_user_linkedin" 
gorm:"type:varchar(255)"`
+       DeployableUserTwitter      string `json:"deployable_user_twitter" 
gorm:"type:varchar(255)"`
+       DeployableUserWebsiteURL   string `json:"deployable_user_website_url" 
gorm:"type:varchar(255)"`
+       DeployableUserOrganization string `json:"deployable_user_organization" 
gorm:"type:varchar(255)"`
+
+       DeployablePipelineCreatedAt time.Time 
`json:"deployable_pipeline_created_at"`
+       DeployablePipelineID        int       `json:"deployable_pipeline_id"`
+       DeployablePipelineRef       string    `json:"deployable_pipeline_ref" 
gorm:"type:varchar(255)"`
+       DeployablePipelineSha       string    `json:"deployable_pipeline_sha" 
gorm:"type:varchar(255)"`
+       DeployablePipelineStatus    string    
`json:"deployable_pipeline_status" gorm:"type:varchar(255)"`
+       DeployablePipelineUpdatedAt time.Time 
`json:"deployable_pipeline_updated_at"`
+       DeployablePipelineWebURL    string    
`json:"deployable_pipeline_web_url" gorm:"type:varchar(255)"`
+
+       UserAvatarURL string `json:"user_avatar_url" gorm:"type:varchar(255)"`
+       UserID        int    `json:"user_id"`
+       UserName      string `json:"user_name" gorm:"type:varchar(255)"`
+       UserState     string `json:"user_state" gorm:"type:varchar(255)"`
+       UserUsername  string `json:"user_username" gorm:"type:varchar(255)"`
+       UserWebURL    string `json:"user_web_url" gorm:"type:varchar(255)"`
+}
+
+func (GitlabDeployment) TableName() string {
+       return "_tool_gitlab_deployments"
+}
diff --git a/backend/plugins/gitlab/models/migrationscripts/register.go 
b/backend/plugins/gitlab/models/migrationscripts/20230926_add_deployment.go
similarity index 55%
copy from backend/plugins/gitlab/models/migrationscripts/register.go
copy to 
backend/plugins/gitlab/models/migrationscripts/20230926_add_deployment.go
index 9287ac8ea..b8674c9b3 100644
--- a/backend/plugins/gitlab/models/migrationscripts/register.go
+++ b/backend/plugins/gitlab/models/migrationscripts/20230926_add_deployment.go
@@ -18,27 +18,22 @@ limitations under the License.
 package migrationscripts
 
 import (
-       "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/errors"
+       
"github.com/apache/incubator-devlake/plugins/gitlab/models/migrationscripts/archived"
 )
 
-// All return all the migration scripts
-func All() []plugin.MigrationScript {
-       return []plugin.MigrationScript{
-               new(addInitTables),
-               new(addGitlabCI),
-               new(addPipelineID),
-               new(addPipelineProjects),
-               new(fixDurationToFloat8),
-               new(addTransformationRule20221125),
-               new(addStdTypeToIssue221230),
-               new(addIsDetailRequired20230210),
-               new(addConnectionIdToTransformationRule),
-               new(addGitlabCommitAuthorInfo),
-               new(addTypeEnvToPipeline),
-               new(renameTr2ScopeConfig),
-               new(addGitlabIssueAssignee),
-               new(addMrCommitSha),
-               new(addRawParamTableForScope),
-               new(addProjectArchived),
-       }
+type addDeployment struct {
+}
+
+func (addDeployment) Up(basicRes context.BasicRes) errors.Error {
+       return basicRes.GetDal().AutoMigrate(&archived.GitlabDeployment{})
+}
+
+func (addDeployment) Version() uint64 {
+       return 20230926140000
+}
+
+func (addDeployment) Name() string {
+       return "add deployment table in tool layer"
 }
diff --git 
a/backend/plugins/gitlab/models/migrationscripts/archived/deployment.go 
b/backend/plugins/gitlab/models/migrationscripts/archived/deployment.go
new file mode 100644
index 000000000..60f9c285b
--- /dev/null
+++ b/backend/plugins/gitlab/models/migrationscripts/archived/deployment.go
@@ -0,0 +1,98 @@
+/*
+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 archived
+
+import (
+       
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+       "time"
+)
+
+type GitlabDeployment struct {
+       archived.NoPKModel `swaggerignore:"true" json:"-" mapstructure:"-"`
+
+       ConnectionId uint64 `json:"connection_id" gorm:"primaryKey"`
+       GitlabId     int    `json:"gitlab_id" gorm:"primaryKey"`
+
+       CreatedDate time.Time `json:"created_date"`
+       UpdatedDate time.Time `json:"updated_date"`
+       Status      string    `json:"status"`
+
+       DeploymentId int    `json:"id" gorm:"primaryKey"`
+       Iid          int    `json:"iid"`
+       Ref          string `json:"ref"`
+       Sha          string `json:"sha"`
+       Environment  string `json:"environment" gorm:"type:varchar(255)"`
+       Name         string `json:"name" gorm:"type:varchar(255)"`
+
+       DeployableCommitAuthorEmail string    
`json:"deployable_commit_author_email" gorm:"type:varchar(255)"`
+       DeployableCommitAuthorName  string    
`json:"deployable_commit_author_name" gorm:"type:varchar(255)"`
+       DeployableCommitCreatedAt   time.Time 
`json:"deployable_commit_created_at"`
+       DeployableCommitID          string    `json:"deployable_commit_id" 
gorm:"type:varchar(255)"`
+       DeployableCommitMessage     string    `json:"deployable_commit_message" 
gorm:"type:varchar(255)"`
+       DeployableCommitShortID     string    
`json:"deployable_commit_short_id" gorm:"type:varchar(255)"`
+       DeployableCommitTitle       string    `json:"deployable_commit_title" 
gorm:"type:varchar(255)"`
+
+       //DeployableCoverage   any       `json:"deployable_coverage"`
+       DeployableCreatedAt  *time.Time `json:"deployable_created_at"`
+       DeployableFinishedAt *time.Time `json:"deployable_finished_at"`
+       DeployableID         int        `json:"deployable_id"`
+       DeployableName       string     `json:"deployable_name" 
gorm:"type:varchar(255)"`
+       DeployableRef        string     `json:"deployable_ref" 
gorm:"type:varchar(255)"`
+       //DeployableRunner     any       `json:"deployable_runner"`
+       DeployableStage     string     `json:"deployable_stage" 
gorm:"type:varchar(255)"`
+       DeployableStartedAt *time.Time `json:"deployable_started_at"`
+       DeployableStatus    string     `json:"deployable_status" 
gorm:"type:varchar(255)"`
+       DeployableTag       bool       `json:"deployable_tag"`
+       DeployableDuration  *float64   `json:"deployable_duration"`
+       QueuedDuration      float64    `json:"queued_duration"`
+
+       DeployableUserID        int       `json:"deployable_user_id"`
+       DeployableUserName      string    `json:"deployable_user_name" 
gorm:"type:varchar(255)"`
+       DeployableUserUsername  string    `json:"deployable_user_username" 
gorm:"type:varchar(255)"`
+       DeployableUserState     string    `json:"deployable_user_state" 
gorm:"type:varchar(255)"`
+       DeployableUserAvatarURL string    `json:"deployable_user_avatar_url" 
gorm:"type:varchar(255)"`
+       DeployableUserWebURL    string    `json:"deployable_user_web_url" 
gorm:"type:varchar(255)"`
+       DeployableUserCreatedAt time.Time `json:"deployable_user_created_at"`
+       //DeployableUserBio          any       `json:"deployable_user_bio"`
+       //DeployableUserLocation     any       `json:"deployable_user_location"`
+       DeployableUserPublicEmail  string `json:"deployable_user_public_email" 
gorm:"type:varchar(255)"`
+       DeployableUserSkype        string `json:"deployable_user_skype" 
gorm:"type:varchar(255)"`
+       DeployableUserLinkedin     string `json:"deployable_user_linkedin" 
gorm:"type:varchar(255)"`
+       DeployableUserTwitter      string `json:"deployable_user_twitter" 
gorm:"type:varchar(255)"`
+       DeployableUserWebsiteURL   string `json:"deployable_user_website_url" 
gorm:"type:varchar(255)"`
+       DeployableUserOrganization string `json:"deployable_user_organization" 
gorm:"type:varchar(255)"`
+
+       DeployablePipelineCreatedAt time.Time 
`json:"deployable_pipeline_created_at"`
+       DeployablePipelineID        int       `json:"deployable_pipeline_id"`
+       DeployablePipelineRef       string    `json:"deployable_pipeline_ref" 
gorm:"type:varchar(255)"`
+       DeployablePipelineSha       string    `json:"deployable_pipeline_sha" 
gorm:"type:varchar(255)"`
+       DeployablePipelineStatus    string    
`json:"deployable_pipeline_status" gorm:"type:varchar(255)"`
+       DeployablePipelineUpdatedAt time.Time 
`json:"deployable_pipeline_updated_at"`
+       DeployablePipelineWebURL    string    
`json:"deployable_pipeline_web_url" gorm:"type:varchar(255)"`
+
+       UserAvatarURL string `json:"user_avatar_url" gorm:"type:varchar(255)"`
+       UserID        int    `json:"user_id"`
+       UserName      string `json:"user_name" gorm:"type:varchar(255)"`
+       UserState     string `json:"user_state" gorm:"type:varchar(255)"`
+       UserUsername  string `json:"user_username" gorm:"type:varchar(255)"`
+       UserWebURL    string `json:"user_web_url" gorm:"type:varchar(255)"`
+}
+
+func (GitlabDeployment) TableName() string {
+       return "_tool_gitlab_deployments"
+}
diff --git a/backend/plugins/gitlab/models/migrationscripts/register.go 
b/backend/plugins/gitlab/models/migrationscripts/register.go
index 9287ac8ea..c6c547d2e 100644
--- a/backend/plugins/gitlab/models/migrationscripts/register.go
+++ b/backend/plugins/gitlab/models/migrationscripts/register.go
@@ -40,5 +40,6 @@ func All() []plugin.MigrationScript {
                new(addMrCommitSha),
                new(addRawParamTableForScope),
                new(addProjectArchived),
+               new(addDeployment),
        }
 }
diff --git a/backend/plugins/gitlab/tasks/deployment_collector.go 
b/backend/plugins/gitlab/tasks/deployment_collector.go
new file mode 100644
index 000000000..721ab558e
--- /dev/null
+++ b/backend/plugins/gitlab/tasks/deployment_collector.go
@@ -0,0 +1,75 @@
+/*
+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 (
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
+       helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "net/url"
+       "time"
+)
+
+var _ plugin.SubTaskEntryPoint = CollectDeployment
+
+const (
+       RAW_DEPLOYMENT = "gitlab_deployments"
+)
+
+func init() {
+       RegisterSubtaskMeta(CollectDeploymentMeta)
+}
+
+var CollectDeploymentMeta = &plugin.SubTaskMeta{
+       Name:             "CollectDeployment",
+       EntryPoint:       CollectDeployment,
+       EnabledByDefault: true,
+       Description:      "Collect gitlab deployment from api into raw layer 
table",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       Dependencies:     []*plugin.SubTaskMeta{},
+}
+
+func CollectDeployment(taskCtx plugin.SubTaskContext) errors.Error {
+       rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, 
RAW_DEPLOYMENT)
+       collectorWithState, err := 
helper.NewStatefulApiCollector(*rawDataSubTaskArgs)
+       if err != nil {
+               return err
+       }
+       err = collectorWithState.InitCollector(helper.ApiCollectorArgs{
+               RawDataSubTaskArgs: *rawDataSubTaskArgs,
+               ApiClient:          data.ApiClient,
+               PageSize:           100,
+               UrlTemplate:        "projects/{{ .Params.ProjectId 
}}/deployments",
+               Query: func(reqData *helper.RequestData) (url.Values, 
errors.Error) {
+                       query, err := GetQuery(reqData)
+                       if err != nil {
+                               return query, err
+                       }
+                       if collectorWithState.Since != nil {
+                               query.Set("updated_after", 
collectorWithState.Since.Format(time.RFC3339))
+                       }
+                       return query, nil
+               },
+               GetTotalPages:  GetTotalPagesFromResponse,
+               ResponseParser: GetRawMessageFromResponse,
+       })
+       if err != nil {
+               return err
+       }
+       return collectorWithState.Execute()
+}
diff --git a/backend/plugins/gitlab/tasks/deployment_convertor.go 
b/backend/plugins/gitlab/tasks/deployment_convertor.go
new file mode 100644
index 000000000..a5faf8986
--- /dev/null
+++ b/backend/plugins/gitlab/tasks/deployment_convertor.go
@@ -0,0 +1,138 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "fmt"
+       "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/devops"
+       "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/gitlab/models"
+       "github.com/spf13/cast"
+       "reflect"
+)
+
+var _ plugin.SubTaskEntryPoint = ConvertDeployment
+
+func init() {
+       RegisterSubtaskMeta(ConvertDeploymentMeta)
+}
+
+var ConvertDeploymentMeta = &plugin.SubTaskMeta{
+       Name:             "ConvertDeployment",
+       EntryPoint:       ConvertDeployment,
+       EnabledByDefault: true,
+       Description:      "Convert gitlab deployment from tool layer to domain 
layer",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       Dependencies:     []*plugin.SubTaskMeta{ExtractDeploymentMeta},
+}
+
+func ConvertDeployment(taskCtx plugin.SubTaskContext) errors.Error {
+       rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, 
RAW_DEPLOYMENT)
+       db := taskCtx.GetDal()
+
+       repo := &models.GitlabProject{}
+       err := db.First(repo, dal.Where("gitlab_id = ? and connection_id = ?", 
data.Options.ProjectId, data.Options.ConnectionId))
+       if err != nil {
+               return err
+       }
+
+       projectIdGen := didgen.NewDomainIdGenerator(&models.GitlabProject{})
+
+       cursor, err := db.Cursor(
+               dal.From(&models.GitlabDeployment{}),
+               dal.Where("connection_id = ? AND gitlab_id = ?", 
data.Options.ConnectionId, data.Options.ProjectId),
+       )
+       if err != nil {
+               return err
+       }
+       defer cursor.Close()
+
+       idGen := didgen.NewDomainIdGenerator(&models.GitlabDeployment{})
+       //pipelineIdGen := 
didgen.NewDomainIdGenerator(&models.BitbucketPipeline{})
+
+       converter, err := api.NewDataConverter(api.DataConverterArgs{
+               InputRowType:       reflect.TypeOf(models.GitlabDeployment{}),
+               Input:              cursor,
+               RawDataSubTaskArgs: *rawDataSubTaskArgs,
+               Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
+                       gitlabDeployment := inputRow.(*models.GitlabDeployment)
+
+                       var duration *uint64
+                       if gitlabDeployment.DeployableDuration != nil {
+                               deployableDuration := 
cast.ToUint64(*gitlabDeployment.DeployableDuration)
+                               duration = &deployableDuration
+                       }
+                       if duration == nil || *duration == 0 {
+                               if gitlabDeployment.DeployableFinishedAt != nil 
&& gitlabDeployment.DeployableCreatedAt != nil {
+                                       deployableDuration := 
uint64(gitlabDeployment.DeployableFinishedAt.Sub(*gitlabDeployment.DeployableCreatedAt).Seconds())
+                                       duration = &deployableDuration
+                               }
+                       }
+                       domainDeployCommit := &devops.CicdDeploymentCommit{
+                               DomainEntity:     
domainlayer.NewDomainEntity(idGen.Generate(data.Options.ConnectionId, 
gitlabDeployment.DeploymentId)),
+                               CicdScopeId:      
projectIdGen.Generate(data.Options.ConnectionId, data.Options.ProjectId),
+                               CicdDeploymentId: 
idGen.Generate(data.Options.ConnectionId, gitlabDeployment.DeploymentId),
+                               Name:             fmt.Sprintf("%s:%d", 
gitlabDeployment.Name, gitlabDeployment.DeploymentId),
+                               Result: devops.GetResult(&devops.ResultRule{
+                                       Failed:  []string{"UNDEPLOYED", 
"failed"},
+                                       Success: []string{"COMPLETED", 
"success"},
+                                       Abort:   []string{"created", 
"canceled"},
+                                       Manual:  []string{"running", "blocked"},
+                                       Default: gitlabDeployment.Status,
+                               }, gitlabDeployment.Status),
+                               Status: 
devops.GetStatus(&devops.StatusRule[string]{
+                                       Done:       []string{"COMPLETED", 
"UNDEPLOYED", "failed", "success", "canceled"},
+                                       InProgress: []string{"running"},
+                                       NotStarted: []string{"created"},
+                                       Manual:     []string{"blocked"},
+                                       Default:    gitlabDeployment.Status,
+                               }, gitlabDeployment.Status),
+                               Environment:  gitlabDeployment.Environment,
+                               CreatedDate:  gitlabDeployment.CreatedDate,
+                               StartedDate:  
gitlabDeployment.DeployableStartedAt,
+                               FinishedDate: 
gitlabDeployment.DeployableFinishedAt,
+                               CommitSha:    gitlabDeployment.Sha,
+                               RefName:      gitlabDeployment.Ref,
+                               RepoId:       
didgen.NewDomainIdGenerator(&models.GitlabProject{}).Generate(data.Options.ConnectionId,
 data.Options.ProjectId),
+                               RepoUrl:      repo.WebUrl,
+                       }
+                       if duration != nil {
+                               domainDeployCommit.DurationSec = duration
+                       }
+                       if data.RegexEnricher != nil {
+                               domainDeployCommit.Environment = 
data.RegexEnricher.ReturnNameIfOmittedOrMatched(devops.PRODUCTION, 
gitlabDeployment.Environment)
+                       }
+
+                       return []interface{}{
+                               domainDeployCommit,
+                               domainDeployCommit.ToDeployment(),
+                       }, nil
+               },
+       })
+
+       if err != nil {
+               return err
+       }
+
+       return converter.Execute()
+}
diff --git a/backend/plugins/gitlab/tasks/deployment_extractor.go 
b/backend/plugins/gitlab/tasks/deployment_extractor.go
new file mode 100644
index 000000000..2e8124301
--- /dev/null
+++ b/backend/plugins/gitlab/tasks/deployment_extractor.go
@@ -0,0 +1,236 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "encoding/json"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/common"
+       "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/gitlab/models"
+       "time"
+)
+
+var _ plugin.SubTaskEntryPoint = ExtractDeployment
+
+func init() {
+       RegisterSubtaskMeta(ExtractDeploymentMeta)
+}
+
+var ExtractDeploymentMeta = &plugin.SubTaskMeta{
+       Name:             "ExtractDeployment",
+       EntryPoint:       ExtractDeployment,
+       EnabledByDefault: true,
+       Description:      "Extract gitlab deployment from raw layer to tool 
layer",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       Dependencies:     []*plugin.SubTaskMeta{CollectDeploymentMeta},
+}
+
+func ExtractDeployment(taskCtx plugin.SubTaskContext) errors.Error {
+       rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, 
RAW_DEPLOYMENT)
+
+       extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+               RawDataSubTaskArgs: *rawDataSubTaskArgs,
+               Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+                       deploymentResp := &GitlabDeploymentResp{}
+                       err := errors.Convert(json.Unmarshal(row.Data, 
deploymentResp))
+                       if err != nil {
+                               return nil, err
+                       }
+                       gitlabDeployment := 
deploymentResp.toGitlabDeployment(data.Options.ConnectionId, 
data.Options.ProjectId)
+                       return []interface{}{
+                               gitlabDeployment,
+                       }, nil
+               },
+       })
+
+       if err != nil {
+               return err
+       }
+
+       return extractor.Execute()
+}
+
+type GitlabDeploymentResp struct {
+       CreatedAt   time.Time                   `json:"created_at"`
+       UpdatedAt   time.Time                   `json:"updated_at"`
+       Status      string                      `json:"status"`
+       Deployable  GitlabDeploymentDeployable  `json:"deployable"`
+       Environment GitlabDeploymentEnvironment `json:"environment"`
+       ID          int                         `json:"id"`
+       Iid         int                         `json:"iid"`
+       Ref         string                      `json:"ref"`
+       Sha         string                      `json:"sha"`
+       User        GitlabDeploymentSimpleUser  `json:"user"`
+}
+
+func (r GitlabDeploymentResp) toGitlabDeployment(connectionId uint64, gitlabId 
int) *models.GitlabDeployment {
+       return &models.GitlabDeployment{
+               NoPKModel:                   common.NewNoPKModel(),
+               ConnectionId:                connectionId,
+               GitlabId:                    gitlabId,
+               CreatedDate:                 r.CreatedAt,
+               UpdatedDate:                 r.UpdatedAt,
+               Status:                      r.Status,
+               DeploymentId:                r.ID,
+               Iid:                         r.Iid,
+               Ref:                         r.Ref,
+               Sha:                         r.Sha,
+               Environment:                 r.Environment.Name,
+               Name:                        r.Deployable.Name,
+               DeployableCommitAuthorEmail: r.Deployable.Commit.AuthorEmail,
+               DeployableCommitAuthorName:  r.Deployable.Commit.AuthorName,
+               DeployableCommitCreatedAt:   r.Deployable.Commit.CreatedAt,
+               DeployableCommitID:          r.Deployable.Commit.ID,
+               DeployableCommitMessage:     r.Deployable.Commit.Message,
+               DeployableCommitShortID:     r.Deployable.Commit.ShortID,
+               DeployableCommitTitle:       r.Deployable.Commit.Title,
+               //DeployableCoverage:          r.Deployable.Coverage,
+               DeployableID:   r.Deployable.ID,
+               DeployableName: r.Deployable.Name,
+               DeployableRef:  r.Deployable.Ref,
+               //DeployableRunner:            r.Deployable.Runner,
+               DeployableStage:         r.Deployable.Stage,
+               DeployableStatus:        r.Deployable.Status,
+               DeployableTag:           r.Deployable.Tag,
+               DeployableUserID:        r.Deployable.User.ID,
+               DeployableUserName:      r.Deployable.User.Name,
+               DeployableUserUsername:  r.Deployable.User.Username,
+               DeployableUserState:     r.Deployable.User.State,
+               DeployableUserAvatarURL: r.Deployable.User.AvatarURL,
+               DeployableUserWebURL:    r.Deployable.User.WebURL,
+               DeployableUserCreatedAt: r.Deployable.User.CreatedAt,
+               //DeployableUserBio:           r.Deployable.User.Bio,
+               //DeployableUserLocation:      r.Deployable.User.Location,
+               DeployableUserPublicEmail:   r.Deployable.User.PublicEmail,
+               DeployableUserSkype:         r.Deployable.User.Skype,
+               DeployableUserLinkedin:      r.Deployable.User.Linkedin,
+               DeployableUserTwitter:       r.Deployable.User.Twitter,
+               DeployableUserWebsiteURL:    r.Deployable.User.WebsiteURL,
+               DeployableUserOrganization:  r.Deployable.User.Organization,
+               DeployablePipelineCreatedAt: r.Deployable.Pipeline.CreatedAt,
+               DeployablePipelineID:        r.Deployable.Pipeline.ID,
+               DeployablePipelineRef:       r.Deployable.Pipeline.Ref,
+               DeployablePipelineSha:       r.Deployable.Pipeline.Sha,
+               DeployablePipelineStatus:    r.Deployable.Pipeline.Status,
+               DeployablePipelineUpdatedAt: r.Deployable.Pipeline.UpdatedAt,
+               DeployablePipelineWebURL:    r.Deployable.Pipeline.WebURL,
+               UserAvatarURL:               r.User.AvatarURL,
+               UserID:                      r.User.ID,
+               UserName:                    r.User.Name,
+               UserState:                   r.User.State,
+               UserUsername:                r.User.Username,
+               UserWebURL:                  r.User.WebURL,
+               DeployableStartedAt:         r.Deployable.StartedAt,
+               DeployableCreatedAt:         r.Deployable.CreatedAt,
+               DeployableFinishedAt:        r.Deployable.FinishedAt,
+               DeployableDuration:          r.Deployable.Duration,
+       }
+}
+
+type GitlabDeploymentCommit struct {
+       AuthorEmail string    `json:"author_email"`
+       AuthorName  string    `json:"author_name"`
+       CreatedAt   time.Time `json:"created_at"`
+       ID          string    `json:"id"`
+       Message     string    `json:"message"`
+       ShortID     string    `json:"short_id"`
+       Title       string    `json:"title"`
+}
+
+type GitlabDeploymentProject struct {
+       CiJobTokenScopeEnabled bool `json:"ci_job_token_scope_enabled"`
+}
+
+type GitlabDeploymentFullUser struct {
+       ID           int       `json:"id"`
+       Name         string    `json:"name"`
+       Username     string    `json:"username"`
+       State        string    `json:"state"`
+       AvatarURL    string    `json:"avatar_url"`
+       WebURL       string    `json:"web_url"`
+       CreatedAt    time.Time `json:"created_at"`
+       Bio          any       `json:"bio"`
+       Location     any       `json:"location"`
+       PublicEmail  string    `json:"public_email"`
+       Skype        string    `json:"skype"`
+       Linkedin     string    `json:"linkedin"`
+       Twitter      string    `json:"twitter"`
+       WebsiteURL   string    `json:"website_url"`
+       Organization string    `json:"organization"`
+}
+
+type GitlabDeploymentPipeline struct {
+       CreatedAt time.Time `json:"created_at"`
+       ID        int       `json:"id"`
+       Ref       string    `json:"ref"`
+       Sha       string    `json:"sha"`
+       Status    string    `json:"status"`
+       UpdatedAt time.Time `json:"updated_at"`
+       WebURL    string    `json:"web_url"`
+}
+
+type GitlabDeploymentDeployable struct {
+       Commit     GitlabDeploymentCommit   `json:"commit"`
+       Coverage   any                      `json:"coverage"`
+       CreatedAt  *time.Time               `json:"created_at"`
+       FinishedAt *time.Time               `json:"finished_at"`
+       ID         int                      `json:"id"`
+       Name       string                   `json:"name"`
+       Ref        string                   `json:"ref"`
+       Runner     any                      `json:"runner"`
+       Stage      string                   `json:"stage"`
+       StartedAt  *time.Time               `json:"started_at"`
+       Status     string                   `json:"status"`
+       Tag        bool                     `json:"tag"`
+       Project    GitlabDeploymentProject  `json:"project"`
+       User       GitlabDeploymentFullUser `json:"user"`
+       Pipeline   GitlabDeploymentPipeline `json:"pipeline"`
+
+       AllowFailure bool       `json:"allow_failure"`
+       ErasedAt     *time.Time `json:"erased_at"`
+
+       Duration          *float64                    `json:"duration"`
+       QueuedDuration    *float64                    `json:"queued_duration"`
+       ArtifactsExpireAt *time.Time                  
`json:"artifacts_expire_at "`
+       TagList           []string                    `json:"tag_list"`
+       Artifacts         []GitlabDeploymentArtifacts `json:"artifacts"`
+}
+
+type GitlabDeploymentEnvironment struct {
+       ExternalURL string `json:"external_url"`
+       ID          int    `json:"id"`
+       Name        string `json:"name"`
+}
+
+type GitlabDeploymentArtifacts struct {
+       FileType   string `json:"file_type"`
+       Size       int    `json:"size"`
+       Filename   string `json:"filename"`
+       FileFormat any    `json:"file_format"`
+}
+
+type GitlabDeploymentSimpleUser struct {
+       AvatarURL string `json:"avatar_url"`
+       ID        int    `json:"id"`
+       Name      string `json:"name"`
+       State     string `json:"state"`
+       Username  string `json:"username"`
+       WebURL    string `json:"web_url"`
+}


Reply via email to