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

mappjzc 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 2a92ebc85 feat(sonarqube): add remote api (#4409)
2a92ebc85 is described below

commit 2a92ebc85bcd9ca1397fe3f1a05f5c9877fd6f0b
Author: Warren Chen <[email protected]>
AuthorDate: Tue Feb 14 10:59:50 2023 +0800

    feat(sonarqube): add remote api (#4409)
---
 backend/plugins/gitlab/api/remote.go               |  28 +--
 .../plugins/{gitlab => sonarqube}/api/remote.go    | 234 ++++++---------------
 backend/plugins/sonarqube/impl/impl.go             |   6 +
 .../plugins/sonarqube/tasks/projects_extractor.go  |  42 ++--
 4 files changed, 114 insertions(+), 196 deletions(-)

diff --git a/backend/plugins/gitlab/api/remote.go 
b/backend/plugins/gitlab/api/remote.go
index 6b7e5ff62..44369b97e 100644
--- a/backend/plugins/gitlab/api/remote.go
+++ b/backend/plugins/gitlab/api/remote.go
@@ -274,25 +274,27 @@ func SearchRemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutp
                search = []string{""}
        }
 
+       var p int
+       var err1 error
        page, ok := input.Query["page"]
        if !ok || len(page) == 0 {
-               page = []string{""}
-       }
-
-       p, err1 := strconv.Atoi(page[0])
-       if err1 != nil {
-               return nil, errors.BadInput.Wrap(err1, fmt.Sprintf("failed to 
Atoi page:%s", page[0]))
+               p = 1
+       } else {
+               p, err1 = strconv.Atoi(page[0])
+               if err != nil {
+                       return nil, errors.BadInput.Wrap(err1, 
fmt.Sprintf("failed to Atoi page:%s", page[0]))
+               }
        }
-
+       var ps int
        pageSize, ok := input.Query["pageSize"]
        if !ok || len(pageSize) == 0 {
-               pageSize = []string{""}
-       }
-       ps, err1 := strconv.Atoi(pageSize[0])
-       if err1 != nil {
-               return nil, errors.BadInput.Wrap(err1, fmt.Sprintf("failed to 
Atoi pageSize:%s", pageSize[0]))
+               ps = GitlabRemoteScopesPerPage
+       } else {
+               ps, err1 = strconv.Atoi(pageSize[0])
+               if err1 != nil {
+                       return nil, errors.BadInput.Wrap(err1, 
fmt.Sprintf("failed to Atoi pageSize:%s", pageSize[0]))
+               }
        }
-
        // create api client
        apiClient, err := api.NewApiClientFromConnection(context.TODO(), 
basicRes, connection)
        if err != nil {
diff --git a/backend/plugins/gitlab/api/remote.go 
b/backend/plugins/sonarqube/api/remote.go
similarity index 54%
copy from backend/plugins/gitlab/api/remote.go
copy to backend/plugins/sonarqube/api/remote.go
index 6b7e5ff62..90f15c596 100644
--- a/backend/plugins/gitlab/api/remote.go
+++ b/backend/plugins/sonarqube/api/remote.go
@@ -29,8 +29,8 @@ 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/gitlab/models"
-       "github.com/apache/incubator-devlake/plugins/gitlab/tasks"
+       "github.com/apache/incubator-devlake/plugins/sonarqube/models"
+       "github.com/apache/incubator-devlake/plugins/sonarqube/tasks"
 )
 
 type RemoteScopesChild struct {
@@ -51,66 +51,41 @@ type PageData struct {
        Tag     string `json:"tag"`
 }
 
-type GroupResponse struct {
-       Id                   int    `json:"id"`
-       WebUrl               string `json:"web_url"`
-       Name                 string `json:"name"`
-       Path                 string `json:"path"`
-       Description          string `json:"description"`
-       Visibility           string `json:"visibility"`
-       LfsEnabled           bool   `json:"lfs_enabled"`
-       AvatarUrl            string `json:"avatar_url"`
-       RequestAccessEnabled bool   `json:"request_access_enabled"`
-       FullName             string `json:"full_name"`
-       FullPath             string `json:"full_path"`
-       ParentId             *int   `json:"parent_id"`
-       LdapCN               string `json:"ldap_cn"`
-       LdapAccess           string `json:"ldap_access"`
-}
-
-const GitlabRemoteScopesPerPage int = 100
+const SonarqubeRemoteScopesPerPage int = 10
 const TypeProject string = "scope"
-const TypeGroup string = "group"
 
 // RemoteScopes list all available scope for users
 // @Summary list all available scope for users
 // @Description list all available scope for users
-// @Tags plugins/gitlab
+// @Tags plugins/sonarqube
 // @Accept application/json
 // @Param connectionId path int false "connection ID"
-// @Param groupId query string false "group ID"
-// @Param pageToken query string false "page Token"
-// @Success 200  {object} []models.GitlabProject
+// @Param pageToken body string false "page Token"
+// @Success 200  {object} []models.SonarqubeProject
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
-// @Router /plugins/gitlab/connections/{connectionId}/remote-scopes [GET]
+// @Router /plugins/sonarqube/connections/{connectionId}/remote-scopes [GET]
 func RemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, 
errors.Error) {
        connectionId, _ := extractParam(input.Params)
        if connectionId == 0 {
                return nil, errors.BadInput.New("invalid connectionId")
        }
 
-       connection := &models.GitlabConnection{}
+       connection := &models.SonarqubeConnection{}
        err := connectionHelper.First(connection, input.Params)
        if err != nil {
                return nil, err
        }
 
-       groupId, ok := input.Query["groupId"]
-       if !ok || len(groupId) == 0 {
-               groupId = []string{""}
-       }
-
        pageToken, ok := input.Query["pageToken"]
        if !ok || len(pageToken) == 0 {
                pageToken = []string{""}
        }
 
-       // get gid and pageData
-       gid := groupId[0]
+       // get pageData
        pageData, err := GetPageDataFromPageToken(pageToken[0])
        if err != nil {
-               return nil, errors.BadInput.New("failed to get paget token")
+               return nil, errors.BadInput.New("fail to get page token")
        }
 
        // create api client
@@ -122,118 +97,45 @@ func RemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, er
        var res *http.Response
        outputBody := &RemoteScopesOutput{}
 
-       // list groups part
-       if pageData.Tag == TypeGroup {
-               query, err := GetQueryFromPageData(pageData)
-               if err != nil {
-                       return nil, err
-               }
-               query.Set("top_level_only", "true")
-
-               if gid == "" {
-                       res, err = apiClient.Get("groups", query, nil)
-               } else {
-                       res, err = 
apiClient.Get(fmt.Sprintf("groups/%s/subgroups", gid), query, nil)
-               }
-               if err != nil {
-                       return nil, err
-               }
-
-               resBody := []GroupResponse{}
-               err = api.UnmarshalResponse(res, &resBody)
-
-               // append group to output
-               for _, group := range resBody {
-                       child := RemoteScopesChild{
-                               Type: TypeGroup,
-                               Id:   strconv.Itoa(group.Id),
-                               // don't need to save group into data
-                               Data: nil,
-                       }
-
-                       // ignore not top_level
-                       if group.ParentId == nil {
-                               if gid != "" {
-                                       continue
-                               }
-                       } else {
-                               if strconv.Itoa(*group.ParentId) != gid {
-                                       continue
-                               }
-                       }
-
-                       // ignore self
-                       if gid == child.Id {
-                               continue
-                       }
-
-                       child.ParentId = &gid
-                       if *child.ParentId == "" {
-                               child.ParentId = nil
-                       }
-
-                       outputBody.Children = append(outputBody.Children, child)
-               }
+       // list projects part
+       query, err := GetQueryFromPageData(pageData)
+       if err != nil {
+               return nil, err
+       }
+       res, err = apiClient.Get("projects/search", query, nil)
 
-               // check groups count
-               if err != nil {
-                       return nil, err
-               }
-               if len(resBody) < pageData.PerPage {
-                       pageData.Tag = TypeProject
-                       pageData.Page = 1
-                       pageData.PerPage = pageData.PerPage - len(resBody)
-               }
+       if err != nil {
+               return nil, err
        }
 
-       // list projects part
-       if pageData.Tag == TypeProject {
-               query, err := GetQueryFromPageData(pageData)
-               if err != nil {
-                       return nil, err
-               }
-               if gid == "" {
-                       res, err = 
apiClient.Get(fmt.Sprintf("users/%d/projects", 
apiClient.GetData(models.GitlabApiClientData_UserId)), query, nil)
-               } else {
-                       query.Set("with_shared", "false")
-                       res, err = 
apiClient.Get(fmt.Sprintf("/groups/%s/projects", gid), query, nil)
-               }
-               if err != nil {
-                       return nil, err
-               }
+       var resBody struct {
+               Data []tasks.SonarqubeApiProject `json:"components"`
+       }
+       err = api.UnmarshalResponse(res, &resBody)
 
-               resBody := []tasks.GitlabApiProject{}
-               err = api.UnmarshalResponse(res, &resBody)
-
-               // append project to output
-               for _, project := range resBody {
-                       child := RemoteScopesChild{
-                               Type: TypeProject,
-                               Id:   strconv.Itoa(project.CreatorId),
-                               Data: tasks.ConvertProject(&project),
-                       }
-                       child.ParentId = &gid
-                       if *child.ParentId == "" {
-                               child.ParentId = nil
-                       }
-
-                       outputBody.Children = append(outputBody.Children, child)
+       // append project to output
+       for _, project := range resBody.Data {
+               child := RemoteScopesChild{
+                       Type: TypeProject,
+                       Id:   project.ProjectKey,
+                       Data: tasks.ConvertProject(&project),
                }
+               outputBody.Children = append(outputBody.Children, child)
+       }
 
-               // check project count
-               if err != nil {
-                       return nil, err
-               }
-               if len(resBody) < pageData.PerPage {
-                       pageData = nil
-               }
+       // check project count
+       if err != nil {
+               return nil, err
+       }
+       if len(resBody.Data) < pageData.PerPage {
+               pageData = nil
        }
 
        // get the next page token
        outputBody.NextPageToken = ""
        if pageData != nil {
                pageData.Page += 1
-               pageData.PerPage = GitlabRemoteScopesPerPage
+               pageData.PerPage = SonarqubeRemoteScopesPerPage
 
                outputBody.NextPageToken, err = 
GetPageTokenFromPageData(pageData)
                if err != nil {
@@ -247,23 +149,22 @@ func RemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, er
 // SearchRemoteScopes use the Search API and only return project
 // @Summary use the Search API and only return project
 // @Description use the Search API and only return project
-// @Tags plugins/gitlab
+// @Tags plugins/sonarqube
 // @Accept application/json
 // @Param connectionId path int false "connection ID"
-// @Param search query string false "group ID"
-// @Param page query int false "page number"
-// @Param pageSize query int false "page size per page"
-// @Success 200  {object} []models.GitlabProject
+// @Param page body int false "page number"
+// @Param pageSize body int false "page size per page"
+// @Success 200  {object} []models.SonarqubeProject
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
-// @Router /plugins/gitlab/connections/{connectionId}/search-remote-scopes 
[GET]
+// @Router /plugins/sonarqube/connections/{connectionId}/search-remote-scopes 
[GET]
 func SearchRemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        connectionId, _ := extractParam(input.Params)
        if connectionId == 0 {
                return nil, errors.BadInput.New("invalid connectionId")
        }
 
-       connection := &models.GitlabConnection{}
+       connection := &models.SonarqubeConnection{}
        err := connectionHelper.First(connection, input.Params)
        if err != nil {
                return nil, err
@@ -273,24 +174,26 @@ func SearchRemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutp
        if !ok || len(search) == 0 {
                search = []string{""}
        }
-
+       var p int
+       var err1 error
        page, ok := input.Query["page"]
        if !ok || len(page) == 0 {
-               page = []string{""}
-       }
-
-       p, err1 := strconv.Atoi(page[0])
-       if err1 != nil {
-               return nil, errors.BadInput.Wrap(err1, fmt.Sprintf("failed to 
Atoi page:%s", page[0]))
+               p = 1
+       } else {
+               p, err1 = strconv.Atoi(page[0])
+               if err != nil {
+                       return nil, errors.BadInput.Wrap(err1, 
fmt.Sprintf("failed to Atoi page:%s", page[0]))
+               }
        }
-
+       var ps int
        pageSize, ok := input.Query["pageSize"]
        if !ok || len(pageSize) == 0 {
-               pageSize = []string{""}
-       }
-       ps, err1 := strconv.Atoi(pageSize[0])
-       if err1 != nil {
-               return nil, errors.BadInput.Wrap(err1, fmt.Sprintf("failed to 
Atoi pageSize:%s", pageSize[0]))
+               ps = SonarqubeRemoteScopesPerPage
+       } else {
+               ps, err1 = strconv.Atoi(pageSize[0])
+               if err1 != nil {
+                       return nil, errors.BadInput.Wrap(err1, 
fmt.Sprintf("failed to Atoi pageSize:%s", pageSize[0]))
+               }
        }
 
        // create api client
@@ -306,19 +209,21 @@ func SearchRemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutp
        }
 
        // request search
-       res, err := apiClient.Get("search", query, nil)
+       res, err := apiClient.Get("projects/search", query, nil)
        if err != nil {
                return nil, err
        }
-       resBody := []tasks.GitlabApiProject{}
+       var resBody struct {
+               Data []tasks.SonarqubeApiProject `json:"components"`
+       }
        err = api.UnmarshalResponse(res, &resBody)
        if err != nil {
                return nil, err
        }
 
        // set projects return
-       projects := []models.GitlabProject{}
-       for _, project := range resBody {
+       var projects []models.SonarqubeProject
+       for _, project := range resBody.Data {
                projects = append(projects, *tasks.ConvertProject(&project))
        }
 
@@ -340,8 +245,7 @@ func GetPageDataFromPageToken(pageToken string) (*PageData, 
errors.Error) {
        if pageToken == "" {
                return &PageData{
                        Page:    1,
-                       PerPage: GitlabRemoteScopesPerPage,
-                       Tag:     "group",
+                       PerPage: SonarqubeRemoteScopesPerPage,
                }, nil
        }
 
@@ -362,8 +266,8 @@ func GetPageDataFromPageToken(pageToken string) (*PageData, 
errors.Error) {
 
 func GetQueryFromPageData(pageData *PageData) (url.Values, errors.Error) {
        query := url.Values{}
-       query.Set("page", fmt.Sprintf("%v", pageData.Page))
-       query.Set("per_page", fmt.Sprintf("%v", pageData.PerPage))
+       query.Set("p", fmt.Sprintf("%v", pageData.Page))
+       query.Set("ps", fmt.Sprintf("%v", pageData.PerPage))
        return query, nil
 }
 
@@ -372,8 +276,6 @@ func GetQueryForSearchProject(search string, page int, 
perPage int) (url.Values,
        if err != nil {
                return nil, err
        }
-       query.Set("search", search)
-       query.Set("scope", "projects")
-
+       query.Set("q", search)
        return query, nil
 }
diff --git a/backend/plugins/sonarqube/impl/impl.go 
b/backend/plugins/sonarqube/impl/impl.go
index d96b843be..653fcb420 100644
--- a/backend/plugins/sonarqube/impl/impl.go
+++ b/backend/plugins/sonarqube/impl/impl.go
@@ -162,6 +162,12 @@ func (p Sonarqube) ApiResources() 
map[string]map[string]plugin.ApiResourceHandle
                        "PATCH":  api.PatchConnection,
                        "DELETE": api.DeleteConnection,
                },
+               "connections/:connectionId/remote-scopes": {
+                       "GET": api.RemoteScopes,
+               },
+               "connections/:connectionId/search-remote-scopes": {
+                       "GET": api.SearchRemoteScopes,
+               },
                "connections/:connectionId/scopes/:projectKey": {
                        "GET":   api.GetScope,
                        "PATCH": api.UpdateScope,
diff --git a/backend/plugins/sonarqube/tasks/projects_extractor.go 
b/backend/plugins/sonarqube/tasks/projects_extractor.go
index fe4d47afc..1be1b76f8 100644
--- a/backend/plugins/sonarqube/tasks/projects_extractor.go
+++ b/backend/plugins/sonarqube/tasks/projects_extractor.go
@@ -42,24 +42,10 @@ func ExtractProjects(taskCtx plugin.SubTaskContext) 
errors.Error {
        extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
                RawDataSubTaskArgs: *rawDataSubTaskArgs,
                Extract: func(resData *helper.RawData) ([]interface{}, 
errors.Error) {
-                       var res 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"`
-                       }
+                       res := SonarqubeApiProject{}
                        err := errors.Convert(json.Unmarshal(resData.Data, 
&res))
-                       body := &models.SonarqubeProject{
-                               ConnectionId:     data.Options.ConnectionId,
-                               ProjectKey:       res.ProjectKey,
-                               Name:             res.Name,
-                               Qualifier:        res.Qualifier,
-                               Visibility:       res.Visibility,
-                               LastAnalysisDate: res.LastAnalysisDate,
-                               Revision:         res.Revision,
-                       }
+                       body := ConvertProject(&res)
+                       body.ConnectionId = data.Options.ConnectionId
                        data.LastAnalysisDate = 
body.LastAnalysisDate.ToNullableTime()
                        if err != nil {
                                return nil, err
@@ -81,3 +67,25 @@ var ExtractProjectsMeta = plugin.SubTaskMeta{
        Description:      "Extract raw data into tool layer table 
sonarqube_projects",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
+
+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
+}

Reply via email to