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 5b7e0da90 feat(sonarqube): add bp v100 support (#4469)
5b7e0da90 is described below
commit 5b7e0da900936bfcb1907d4a30dc9cc58e8ce8ae
Author: Warren Chen <[email protected]>
AuthorDate: Tue Feb 21 20:59:02 2023 +0800
feat(sonarqube): add bp v100 support (#4469)
---
backend/plugins/sonarqube/api/blueprint_v200.go | 30 +++++++
backend/plugins/sonarqube/e2e/project_test.go | 25 +-----
.../e2e/raw_tables/_raw_sonarqube_projects.csv | 12 ---
.../_tool_sonarqube_projects.csv | 0
backend/plugins/sonarqube/impl/impl.go | 26 ++++++-
.../sonarqube/tasks/filemetrics_convertor.go | 2 +
.../plugins/sonarqube/tasks/projects_collector.go | 82 -------------------
.../plugins/sonarqube/tasks/projects_extractor.go | 91 ----------------------
backend/plugins/sonarqube/tasks/shared.go | 23 ++++++
9 files changed, 83 insertions(+), 208 deletions(-)
diff --git a/backend/plugins/sonarqube/api/blueprint_v200.go
b/backend/plugins/sonarqube/api/blueprint_v200.go
index d2920f8bc..611cec815 100644
--- a/backend/plugins/sonarqube/api/blueprint_v200.go
+++ b/backend/plugins/sonarqube/api/blueprint_v200.go
@@ -27,7 +27,11 @@ import (
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/core/utils"
helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ aha
"github.com/apache/incubator-devlake/helpers/pluginhelper/api/apihelperabstract"
"github.com/apache/incubator-devlake/plugins/sonarqube/models"
+ "github.com/apache/incubator-devlake/plugins/sonarqube/tasks"
+ "net/http"
+ "net/url"
"time"
)
@@ -104,3 +108,29 @@ func makeScopesV200(bpScopes []*plugin.BlueprintScopeV200,
connectionId uint64)
}
return scopes, nil
}
+
+func GetApiProject(
+ projectKey string,
+ apiClient aha.ApiClientAbstract,
+) (*tasks.SonarqubeApiProject, errors.Error) {
+ var resData struct {
+ Data []tasks.SonarqubeApiProject `json:"components"`
+ }
+ query := url.Values{}
+ query.Set("q", projectKey)
+ res, err := apiClient.Get("projects/search", query, nil)
+ if err != nil {
+ return nil, err
+ }
+ if res.StatusCode != http.StatusOK {
+ return nil,
errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("unexpected status code when
requesting project detail from %s", res.Request.URL.String()))
+ }
+ err = helper.UnmarshalResponse(res, &resData)
+ if err != nil {
+ return nil, err
+ }
+ if len(resData.Data) > 0 {
+ return &resData.Data[0], nil
+ }
+ return nil, errors.BadInput.New(fmt.Sprintf("Cannot find project: %s",
projectKey))
+}
diff --git a/backend/plugins/sonarqube/e2e/project_test.go
b/backend/plugins/sonarqube/e2e/project_test.go
index aeea1adec..930b9a835 100644
--- a/backend/plugins/sonarqube/e2e/project_test.go
+++ b/backend/plugins/sonarqube/e2e/project_test.go
@@ -33,36 +33,19 @@ func TestSonarqubeProjectDataFlow(t *testing.T) {
var sonarqube impl.Sonarqube
dataflowTester := e2ehelper.NewDataFlowTester(t, "sonarqube", sonarqube)
- taskData := &tasks.SonarqubeTaskData{
- Options: &tasks.SonarqubeOptions{
- ConnectionId: 1,
- ProjectKey: "fa2cf9cd-c448-4fc3-99a5-1c893f15d84c",
- },
- }
-
// import raw data table
-
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_sonarqube_projects.csv",
- "_raw_sonarqube_projects")
+
dataflowTester.ImportCsvIntoTabler("./raw_tables/_tool_sonarqube_projects.csv",
+ &models.SonarqubeProject{})
- // verify extraction
- dataflowTester.FlushTabler(&models.SonarqubeProject{})
- dataflowTester.Subtask(tasks.ExtractProjectsMeta, taskData)
-
- taskData2 := &tasks.SonarqubeTaskData{
+ taskData := &tasks.SonarqubeTaskData{
Options: &tasks.SonarqubeOptions{
ConnectionId: 2,
ProjectKey: "e2c6d5e9-a321-4e8c-b322-03d9599ef962",
},
}
- dataflowTester.Subtask(tasks.ExtractProjectsMeta, taskData2)
- dataflowTester.VerifyTableWithOptions(&models.SonarqubeProject{},
e2ehelper.TableOptions{
- CSVRelPath: "./snapshot_tables/_tool_sonarqube_projects.csv",
- IgnoreTypes: []interface{}{common.NoPKModel{}},
- })
-
dataflowTester.FlushTabler(&codequality.CqProject{})
- dataflowTester.Subtask(tasks.ConvertProjectsMeta, taskData2)
+ dataflowTester.Subtask(tasks.ConvertProjectsMeta, taskData)
dataflowTester.VerifyTableWithOptions(&codequality.CqProject{},
e2ehelper.TableOptions{
CSVRelPath: "./snapshot_tables/projects.csv",
IgnoreTypes: []interface{}{common.NoPKModel{}},
diff --git
a/backend/plugins/sonarqube/e2e/raw_tables/_raw_sonarqube_projects.csv
b/backend/plugins/sonarqube/e2e/raw_tables/_raw_sonarqube_projects.csv
deleted file mode 100644
index a4da44afd..000000000
--- a/backend/plugins/sonarqube/e2e/raw_tables/_raw_sonarqube_projects.csv
+++ /dev/null
@@ -1,12 +0,0 @@
-"id","params","data","url","input","created_at"
-642,"{""connectionId"":1,""ProjectKey"":""fa2cf9cd-c448-4fc3-99a5-1c893f15d84c""}","{""key"":""fa2cf9cd-c448-4fc3-99a5-1c893f15d84c"",""name"":""activeadmin"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2022-12-23T13:52:39+0000"",""revision"":""51689e48ec3faa51127b8462b2219e5e0c09ad8a""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-643,"{""connectionId"":1,""ProjectKey"":""""}","{""key"":""a67b8166-5768-46a5-8830-6d86d797419a"",""name"":""actix-web"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2023-01-04T22:36:46+0000"",""revision"":""08c2cdf6416287d63387584213ea3dd41b007ef6""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-644,"{""connectionId"":2,""ProjectKey"":""""}","{""key"":""a7c268ed-525b-438d-8729-18e83229e66c"",""name"":""Aerial"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2022-12-22T18:26:29+0000"",""revision"":""405bc2494af895f8b9ccbf12f3d0c9530a154011""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-645,"{""connectionId"":2,""ProjectKey"":""""}","{""key"":""f5a50c63-2e8f-4107-9014-853f6f467757"",""name"":""aerosolve"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2023-01-02T06:58:08+0000"",""revision"":""442e76ebb7cbc7f60b04fbfac30dbf862aaffc67""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-646,"{""connectionId"":2,""ProjectKey"":""""}","{""key"":""f960efd5-84eb-44c9-888c-996457ed76d7"",""name"":""AFNetworking"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2022-12-22T12:28:19+0000"",""revision"":""4eaec5b586ddd897ebeda896e332a62a9fdab818""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-647,"{""connectionId"":2,""ProjectKey"":""""}","{""key"":""8b075027-0a3f-44ff-89e1-751d173b4f9d"",""name"":""akka"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2022-12-20T16:27:23+0000"",""revision"":""b7b79a2da1231520fe66532b7af53179d1714952""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-648,"{""connectionId"":3,""ProjectKey"":""""}","{""key"":""eb4e9de1-85f1-4568-9352-94a264ba5bef"",""name"":""akka"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2023-01-01T03:54:58+0000"",""revision"":""50372b37c5cf9ef467c4637f4e526a5fe95fc9c1""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-649,"{""connectionId"":3,""ProjectKey"":""""}","{""key"":""4f3b6f44-dbcd-4507-9cc8-c3e460f1c69e"",""name"":""Alamofire"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2023-01-05T01:14:55+0000"",""revision"":""ce7f662709a152e840b130a158b9afef3402e146""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-650,"{""connectionId"":3,""ProjectKey"":""f1f70ac0-e607-42b8-8fda-46b28f7d3430""}","{""key"":""f1f70ac0-e607-42b8-8fda-46b28f7d3430"",""name"":""algorithms"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2022-12-26T10:57:53+0000"",""revision"":""52b9408c98eaa8458083b27410c0c364406d6e24""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-651,"{""connectionId"":2,""ProjectKey"":""e2c6d5e9-a321-4e8c-b322-03d9599ef962""}","{""key"":""e2c6d5e9-a321-4e8c-b322-03d9599ef962"",""name"":""Android-Universal-Image-Loader"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2022-12-24T18:42:09+0000"",""revision"":""458df4da2e23ba9ad76c79241a948cdfcccf72ae""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
-652,"{""connectionId"":1,""ProjectKey"":""""}","{""key"":""0892ae2f-6a07-4613-a1dc-bf29158afc52"",""name"":""AndroidUtilCode"",""qualifier"":""TRK"",""visibility"":""public"",""lastAnalysisDate"":""2023-01-05T01:25:34+0000"",""revision"":""51c8124045ec5b7c6d342b94a4020ed2f7fda496""}","http://35.89.154.221:30008/api/projects/search?p=1&ps=100","null","2023-02-10
02:46:14.044"
diff --git
a/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_projects.csv
b/backend/plugins/sonarqube/e2e/raw_tables/_tool_sonarqube_projects.csv
similarity index 100%
rename from
backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_projects.csv
rename to backend/plugins/sonarqube/e2e/raw_tables/_tool_sonarqube_projects.csv
diff --git a/backend/plugins/sonarqube/impl/impl.go
b/backend/plugins/sonarqube/impl/impl.go
index 653fcb420..e4655274d 100644
--- a/backend/plugins/sonarqube/impl/impl.go
+++ b/backend/plugins/sonarqube/impl/impl.go
@@ -82,8 +82,6 @@ func (p Sonarqube) GetTablesInfo() []dal.Tabler {
func (p Sonarqube) SubTaskMetas() []plugin.SubTaskMeta {
return []plugin.SubTaskMeta{
- tasks.CollectProjectsMeta,
- tasks.ExtractProjectsMeta,
tasks.CollectIssuesMeta,
tasks.ExtractIssuesMeta,
tasks.CollectHotspotsMeta,
@@ -125,6 +123,30 @@ func (p Sonarqube) PrepareTaskData(taskCtx
plugin.TaskContext, options map[strin
Options: op,
ApiClient: apiClient,
}
+ if op.ProjectKey != "" {
+ var scope *models.SonarqubeProject
+ var apiProject *tasks.SonarqubeApiProject
+ // support v100 & advance mode
+ // If we still cannot find the record in db, we have to request
from remote server and save it to db
+ db := taskCtx.GetDal()
+ err = db.First(&scope, dal.Where("connection_id = ? AND
project_key = ?", op.ConnectionId, op.ProjectKey))
+ if err != nil && db.IsErrorNotFound(err) {
+ apiProject, err = api.GetApiProject(op.ProjectKey,
apiClient)
+ if err != nil {
+ return nil, err
+ }
+ logger.Debug(fmt.Sprintf("Current project: %s",
apiProject.ProjectKey))
+ scope = tasks.ConvertProject(apiProject)
+ scope.ConnectionId = op.ConnectionId
+ err = taskCtx.GetDal().CreateIfNotExist(&scope)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if err != nil {
+ return nil, errors.Default.Wrap(err, fmt.Sprintf("fail
to find project: %s", op.ProjectKey))
+ }
+ }
var createdDateAfter time.Time
if op.CreatedDateAfter != "" {
createdDateAfter, err =
errors.Convert01(time.Parse(time.RFC3339, op.CreatedDateAfter))
diff --git a/backend/plugins/sonarqube/tasks/filemetrics_convertor.go
b/backend/plugins/sonarqube/tasks/filemetrics_convertor.go
index 4440d3208..2cc7d911d 100644
--- a/backend/plugins/sonarqube/tasks/filemetrics_convertor.go
+++ b/backend/plugins/sonarqube/tasks/filemetrics_convertor.go
@@ -29,6 +29,8 @@ import (
"reflect"
)
+const RAW_PROJECTS_TABLE = "sonarqube_projects"
+
var ConvertFileMetricsMeta = plugin.SubTaskMeta{
Name: "convertFileMetrics",
EntryPoint: ConvertFileMetrics,
diff --git a/backend/plugins/sonarqube/tasks/projects_collector.go
b/backend/plugins/sonarqube/tasks/projects_collector.go
deleted file mode 100644
index ba57b81bf..000000000
--- a/backend/plugins/sonarqube/tasks/projects_collector.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-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"
- "fmt"
- "github.com/apache/incubator-devlake/core/errors"
- "github.com/apache/incubator-devlake/core/plugin"
- helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
- "net/http"
- "net/url"
-)
-
-const RAW_PROJECTS_TABLE = "sonarqube_projects"
-
-var _ plugin.SubTaskEntryPoint = CollectProjects
-
-func CollectProjects(taskCtx plugin.SubTaskContext) errors.Error {
- rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_PROJECTS_TABLE)
- logger := taskCtx.GetLogger()
- logger.Info("collect projects")
-
- collectorWithState, err :=
helper.NewApiCollectorWithState(*rawDataSubTaskArgs, data.CreatedDateAfter)
- if err != nil {
- return err
- }
- err = collectorWithState.InitCollector(helper.ApiCollectorArgs{
- ApiClient: data.ApiClient,
- PageSize: 100,
- UrlTemplate: "projects/search",
- Query: func(reqData *helper.RequestData) (url.Values,
errors.Error) {
- query := url.Values{}
- if data.CreatedDateAfter != nil {
- query.Set("analyzedBefore",
-
data.CreatedDateAfter.Format("2006-01-02"))
- }
- if data.Options.ProjectKey != "" {
- query.Set("q", data.Options.ProjectKey)
- }
- query.Set("p", fmt.Sprintf("%v", reqData.Pager.Page))
- query.Set("ps", fmt.Sprintf("%v", reqData.Pager.Size))
- return query, nil
- },
- GetTotalPages: GetTotalPagesFromResponse,
- ResponseParser: func(res *http.Response) ([]json.RawMessage,
errors.Error) {
- var resData struct {
- Data []json.RawMessage `json:"components"`
- }
- err = helper.UnmarshalResponse(res, &resData)
- return resData.Data, err
- },
- })
- if err != nil {
- return err
- }
-
- return collectorWithState.Execute()
-}
-
-var CollectProjectsMeta = plugin.SubTaskMeta{
- Name: "CollectProjects",
- EntryPoint: CollectProjects,
- EnabledByDefault: true,
- Description: "Collect Projects data from Sonarqube api",
- DomainTypes: []string{plugin.DOMAIN_TYPE_CODE_QUALITY},
-}
diff --git a/backend/plugins/sonarqube/tasks/projects_extractor.go
b/backend/plugins/sonarqube/tasks/projects_extractor.go
deleted file mode 100644
index aaf28f13d..000000000
--- a/backend/plugins/sonarqube/tasks/projects_extractor.go
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-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"
- helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
- "github.com/apache/incubator-devlake/plugins/sonarqube/models"
-)
-
-var _ plugin.SubTaskEntryPoint = ExtractProjects
-
-func ExtractProjects(taskCtx plugin.SubTaskContext) errors.Error {
- // As we need to assign data.LastAnalysisDate, we can not use
CreateRawDataSubTaskArgs
- data := taskCtx.GetData().(*SonarqubeTaskData)
- var params = SonarqubeApiParams{
- ConnectionId: data.Options.ConnectionId,
- ProjectKey: data.Options.ProjectKey,
- }
- rawDataSubTaskArgs := &helper.RawDataSubTaskArgs{
- Ctx: taskCtx,
- Params: params,
- Table: RAW_PROJECTS_TABLE,
- }
- extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
- RawDataSubTaskArgs: *rawDataSubTaskArgs,
- Extract: func(resData *helper.RawData) ([]interface{},
errors.Error) {
- res := SonarqubeApiProject{}
- err := errors.Convert(json.Unmarshal(resData.Data,
&res))
- if err != nil {
- return nil, err
- }
- body := ConvertProject(&res)
- body.ConnectionId = data.Options.ConnectionId
- data.LastAnalysisDate =
body.LastAnalysisDate.ToNullableTime()
- return []interface{}{body}, nil
- },
- })
- if err != nil {
- return err
- }
-
- return extractor.Execute()
-}
-
-var ExtractProjectsMeta = plugin.SubTaskMeta{
- Name: "ExtractProjects",
- EntryPoint: ExtractProjects,
- EnabledByDefault: true,
- Description: "Extract raw data into tool layer table
sonarqube_projects",
- DomainTypes: []string{plugin.DOMAIN_TYPE_CODE_QUALITY},
-}
-
-type SonarqubeApiProject struct {
- ProjectKey string `json:"key"`
- Name string `json:"name"`
- Qualifier string `json:"qualifier"`
- Visibility string `json:"visibility"`
- LastAnalysisDate *helper.Iso8601Time `json:"lastAnalysisDate"`
- Revision string `json:"revision"`
-}
-
-// Convert the API response to our DB model instance
-func ConvertProject(sonarqubeApiProject *SonarqubeApiProject)
*models.SonarqubeProject {
- sonarqubeProject := &models.SonarqubeProject{
- ProjectKey: sonarqubeApiProject.ProjectKey,
- Name: sonarqubeApiProject.Name,
- Qualifier: sonarqubeApiProject.Qualifier,
- Visibility: sonarqubeApiProject.Visibility,
- LastAnalysisDate: sonarqubeApiProject.LastAnalysisDate,
- Revision: sonarqubeApiProject.Revision,
- }
- return sonarqubeProject
-}
diff --git a/backend/plugins/sonarqube/tasks/shared.go
b/backend/plugins/sonarqube/tasks/shared.go
index f0c4746cf..024cffcb7 100644
--- a/backend/plugins/sonarqube/tasks/shared.go
+++ b/backend/plugins/sonarqube/tasks/shared.go
@@ -21,6 +21,7 @@ import (
"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/sonarqube/models"
"net/http"
)
@@ -62,3 +63,25 @@ type Paging struct {
PageSize int `json:"pageSize"`
Total int `json:"total"`
}
+
+type SonarqubeApiProject struct {
+ ProjectKey string `json:"key"`
+ Name string `json:"name"`
+ Qualifier string `json:"qualifier"`
+ Visibility string `json:"visibility"`
+ LastAnalysisDate *api.Iso8601Time `json:"lastAnalysisDate"`
+ Revision string `json:"revision"`
+}
+
+// Convert the API response to our DB model instance
+func ConvertProject(sonarqubeApiProject *SonarqubeApiProject)
*models.SonarqubeProject {
+ sonarqubeProject := &models.SonarqubeProject{
+ ProjectKey: sonarqubeApiProject.ProjectKey,
+ Name: sonarqubeApiProject.Name,
+ Qualifier: sonarqubeApiProject.Qualifier,
+ Visibility: sonarqubeApiProject.Visibility,
+ LastAnalysisDate: sonarqubeApiProject.LastAnalysisDate,
+ Revision: sonarqubeApiProject.Revision,
+ }
+ return sonarqubeProject
+}