This is an automated email from the ASF dual-hosted git repository.
abeizn 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 e121d8bdf feat(gitlab) fetch trigger jobs (#6167)
e121d8bdf is described below
commit e121d8bdf8183951bf658fb000ef3cf1b2ff9623
Author: Marco-De-Stefani <[email protected]>
AuthorDate: Thu Jan 25 09:55:32 2024 +0100
feat(gitlab) fetch trigger jobs (#6167)
* feat(gitlab): fetching the "trigger" job as well
* feat(gitlab): add test for parsing trigger job
---
.../e2e/raw_tables/_raw_gitlab_api_trigger_job.csv | 2 +
.../_tool_gitlab_jobs_with_triggered.csv | 2 +
backend/plugins/gitlab/e2e/trigger_job_test.go | 73 ++++++++++++++
.../plugins/gitlab/tasks/trigger_job_collector.go | 92 ++++++++++++++++++
.../plugins/gitlab/tasks/trigger_job_extractor.go | 106 +++++++++++++++++++++
5 files changed, 275 insertions(+)
diff --git
a/backend/plugins/gitlab/e2e/raw_tables/_raw_gitlab_api_trigger_job.csv
b/backend/plugins/gitlab/e2e/raw_tables/_raw_gitlab_api_trigger_job.csv
new file mode 100644
index 000000000..d4ae96eea
--- /dev/null
+++ b/backend/plugins/gitlab/e2e/raw_tables/_raw_gitlab_api_trigger_job.csv
@@ -0,0 +1,2 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""ProjectId"":44}","{""id"":1,""status"":""success"",""stage"":""deploy"",""name"":""deploy"",""ref"":""master"",""tag"":false,""coverage"":null,""allow_failure"":false,""created_at"":""2022-08-25T21:19:10.938Z"",""started_at"":null,""finished_at"":""2022-08-25T23:00:08.594Z"",""duration"":null,""user"":{""id"":39,""name"":""
chinese merico chinese
"",""username"":""merico"",""state"":""active"",""avatar_url"":""https://secure.gravatar.com/avatar/8d7684192b6b64e6a
[...]
\ No newline at end of file
diff --git
a/backend/plugins/gitlab/e2e/snapshot_tables/_tool_gitlab_jobs_with_triggered.csv
b/backend/plugins/gitlab/e2e/snapshot_tables/_tool_gitlab_jobs_with_triggered.csv
new file mode 100644
index 000000000..70d257432
--- /dev/null
+++
b/backend/plugins/gitlab/e2e/snapshot_tables/_tool_gitlab_jobs_with_triggered.csv
@@ -0,0 +1,2 @@
+connection_id,gitlab_id,project_id,pipeline_id,status,stage,name,ref,tag,allow_failure,duration,web_url,gitlab_created_at,started_at,finished_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,1,44,73,success,deploy,deploy,master,0,0,0,"",2022-08-25T21:19:10.938+00:00,"",2022-08-25T23:00:08.594+00:00,"{""ConnectionId"":1,""ProjectId"":44}",_raw_gitlab_api_trigger_job,1,
diff --git a/backend/plugins/gitlab/e2e/trigger_job_test.go
b/backend/plugins/gitlab/e2e/trigger_job_test.go
new file mode 100644
index 000000000..4d697dde4
--- /dev/null
+++ b/backend/plugins/gitlab/e2e/trigger_job_test.go
@@ -0,0 +1,73 @@
+/*
+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 e2e
+
+import (
+ "testing"
+
+ "github.com/apache/incubator-devlake/core/models/domainlayer/devops"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/gitlab/impl"
+ "github.com/apache/incubator-devlake/plugins/gitlab/models"
+ "github.com/apache/incubator-devlake/plugins/gitlab/tasks"
+)
+
+func TestGitlabTriggerJobDataFlow(t *testing.T) {
+
+ var gitlab impl.Gitlab
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "gitlab", gitlab)
+ regexEnricher := api.NewRegexEnricher()
+ _ = regexEnricher.TryAdd(devops.DEPLOYMENT, "(?i)compile")
+ taskData := &tasks.GitlabTaskData{
+ Options: &tasks.GitlabOptions{
+ ConnectionId: 1,
+ ProjectId: 44,
+ },
+ RegexEnricher: regexEnricher,
+ }
+ // import raw data table
+ // SELECT * FROM _raw_gitlab_api_job INTO OUTFILE
"/tmp/_raw_gitlab_api_job.csv" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY
'"' LINES TERMINATED BY '\r\n';
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_gitlab_api_trigger_job.csv",
"_raw_gitlab_api_trigger_job")
+
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_gitlab_api_job.csv",
"_raw_gitlab_api_job")
+
+ // verify extraction
+ dataflowTester.FlushTabler(&models.GitlabJob{})
+ dataflowTester.Subtask(tasks.ExtractApiTriggerJobsMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.GitlabJob{},
+ "./snapshot_tables/_tool_gitlab_jobs_with_triggered.csv",
+ e2ehelper.ColumnWithRawData(
+ "connection_id",
+ "gitlab_id",
+ "project_id",
+ "pipeline_id",
+ "status",
+ "stage",
+ "name",
+ "ref",
+ "tag",
+ "allow_failure",
+ "duration",
+ "web_url",
+ "gitlab_created_at",
+ "started_at",
+ "finished_at",
+ ),
+ )
+}
diff --git a/backend/plugins/gitlab/tasks/trigger_job_collector.go
b/backend/plugins/gitlab/tasks/trigger_job_collector.go
new file mode 100644
index 000000000..210f0f046
--- /dev/null
+++ b/backend/plugins/gitlab/tasks/trigger_job_collector.go
@@ -0,0 +1,92 @@
+/*
+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/dal"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "reflect"
+ "time"
+)
+
+func init() {
+ RegisterSubtaskMeta(&CollectApiTriggerJobsMeta)
+}
+
+const RAW_TRIGGER_JOB_TABLE = "gitlab_api_trigger_job"
+
+var CollectApiTriggerJobsMeta = plugin.SubTaskMeta{
+ Name: "collectApiTriggerJobs",
+ EntryPoint: CollectApiTriggerJobs,
+ EnabledByDefault: true,
+ Description: "Collect job data from gitlab api, supports both
timeFilter and diffSync.",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CICD},
+ Dependencies: []*plugin.SubTaskMeta{&ExtractApiPipelineDetailsMeta},
+}
+
+func CollectApiTriggerJobs(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_TRIGGER_JOB_TABLE)
+ collectorWithState, err :=
helper.NewStatefulApiCollector(*rawDataSubTaskArgs, data.TimeAfter)
+ tickInterval, err := helper.CalcTickInterval(200, 1*time.Minute)
+ incremental := collectorWithState.IsIncremental()
+
+ iterator, err := GetAllPipelinesIterator(taskCtx, collectorWithState)
+
+ err = collectorWithState.InitCollector(helper.ApiCollectorArgs{
+ //RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ MinTickInterval: &tickInterval,
+ PageSize: 100,
+ Incremental: incremental,
+ Input: iterator,
+ UrlTemplate: "projects/{{ .Params.ProjectId }}/pipelines/{{
.Input.GitlabId }}/bridges",
+ ResponseParser: GetRawMessageFromResponse,
+ AfterResponse: ignoreHTTPStatus403, // ignore 403 for CI/CD
disable
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return collectorWithState.Execute()
+}
+
+func GetAllPipelinesIterator(taskCtx plugin.SubTaskContext, collectorWithState
*helper.ApiCollectorStateManager) (*helper.DalCursorIterator, errors.Error) {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*GitlabTaskData)
+ clauses := []dal.Clause{
+ dal.Select("gp.gitlab_id, gp.gitlab_id as iid"),
+ dal.From("_tool_gitlab_pipelines gp"),
+ dal.Where(
+ `gp.project_id = ? and gp.connection_id = ? and
gp.gitlab_id not in (select json_extract(tj.input, '$.GitlabId') as gitlab_id
from _raw_gitlab_api_trigger_job tj)`,
+ data.Options.ProjectId, data.Options.ConnectionId,
+ ),
+ }
+ if collectorWithState.LatestState.LatestSuccessStart != nil {
+ clauses = append(clauses, dal.Where("gitlab_updated_at > ?",
*collectorWithState.LatestState.LatestSuccessStart))
+ }
+ // construct the input iterator
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return nil, err
+ }
+
+ return helper.NewDalCursorIterator(db, cursor,
reflect.TypeOf(GitlabInput{}))
+}
diff --git a/backend/plugins/gitlab/tasks/trigger_job_extractor.go
b/backend/plugins/gitlab/tasks/trigger_job_extractor.go
new file mode 100644
index 000000000..f9b64eeb4
--- /dev/null
+++ b/backend/plugins/gitlab/tasks/trigger_job_extractor.go
@@ -0,0 +1,106 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/gitlab/models"
+ "time"
+)
+
+func init() {
+ RegisterSubtaskMeta(&ExtractApiTriggerJobsMeta)
+}
+
+type ApiTriggerJob struct {
+ Id int `json:"id"`
+ Status string
+ Stage string
+ Name string
+ Ref string
+ Duration float64
+ Pipeline struct {
+ Id int
+ }
+ CreatedAt *time.Time `json:"created_at"`
+ StartedAt *time.Time `json:"started_at"`
+ FinishedAt *time.Time `json:"finished_at"`
+}
+
+var ExtractApiTriggerJobsMeta = plugin.SubTaskMeta{
+ Name: "extractApiTriggerJobs",
+ EntryPoint: ExtractApiTriggerJobs,
+ EnabledByDefault: true,
+ Description: "Extract raw Gitlab trigger jobs data into tool layer
table GitlabPipeline",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CICD},
+ Dependencies: []*plugin.SubTaskMeta{&CollectApiTriggerJobsMeta},
+}
+
+func ExtractApiTriggerJobs(taskCtx plugin.SubTaskContext) errors.Error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_TRIGGER_JOB_TABLE)
+
+ extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
+ // create gitlab commit
+ gitlabApiTriggerJob := &ApiTriggerJob{}
+ err := errors.Convert(json.Unmarshal(row.Data,
gitlabApiTriggerJob))
+ if err != nil {
+ return nil, err
+ }
+
+ gitlabPipeline, err :=
convertTriggerJob(gitlabApiTriggerJob, data.Options.ProjectId)
+ if err != nil {
+ return nil, err
+ }
+
+ // use data.Options.ProjectId to set the value of
ProjectId for it
+ gitlabPipeline.ProjectId = data.Options.ProjectId
+ gitlabPipeline.ConnectionId = data.Options.ConnectionId
+ results := make([]interface{}, 0, 1)
+ results = append(results, gitlabPipeline)
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
+
+func convertTriggerJob(job *ApiTriggerJob, projectId int) (*models.GitlabJob,
errors.Error) {
+ return &models.GitlabJob{
+ GitlabId: job.Id,
+ ProjectId: projectId,
+ Status: job.Status,
+ Stage: job.Stage,
+ Name: job.Name,
+ Ref: job.Ref,
+ Duration: job.Duration,
+ PipelineId: job.Pipeline.Id,
+ GitlabCreatedAt: job.CreatedAt,
+ StartedAt: job.StartedAt,
+ FinishedAt: job.FinishedAt,
+ }, nil
+}