You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by ab...@apache.org on 2023/02/09 05:51:46 UTC

[incubator-devlake] branch main updated: feat(sonarqube): add blueprint200 (#4365)

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 6c0163c88 feat(sonarqube): add blueprint200 (#4365)
6c0163c88 is described below

commit 6c0163c88f6ab25e589727f0672282bb11c989c8
Author: Warren Chen <yi...@merico.dev>
AuthorDate: Thu Feb 9 13:51:41 2023 +0800

    feat(sonarqube): add blueprint200 (#4365)
    
    * feat(sonarqube): add blueprint
    
    * fix(sonarqube): modify minor issues
    
    * fix(sonarqube): modify swagger
---
 .../plugin/tasks/api_collector.go-template         |   1 +
 .../template/plugin/tasks/extractor.go-template    |   1 +
 backend/plugins/customize/api/swagger.go           |   2 +-
 backend/plugins/github/api/swagger.go              |   2 +-
 backend/plugins/gitlab/api/swagger.go              |   2 +-
 backend/plugins/jenkins/api/connection.go          |   2 +-
 backend/plugins/jira/api/swagger.go                |   2 +-
 backend/plugins/jira/impl/impl.go                  |  20 ++--
 backend/plugins/sonarqube/api/blueprint_v200.go    | 106 +++++++++++++++++++++
 .../plugins/sonarqube/api/blueprint_v200_test.go   |  98 +++++++++++++++++++
 backend/plugins/{jira => sonarqube}/api/swagger.go |  41 ++++----
 backend/plugins/sonarqube/impl/impl.go             |  44 +++++++--
 .../plugins/sonarqube/tasks/accounts_collector.go  |   1 +
 .../plugins/sonarqube/tasks/accounts_extractor.go  |   1 +
 .../sonarqube/tasks/filemetrics_collector.go       |   1 +
 .../sonarqube/tasks/filemetrics_extractor.go       |   1 +
 .../plugins/sonarqube/tasks/hotspots_collector.go  |   1 +
 .../plugins/sonarqube/tasks/hotspots_extractor.go  |   1 +
 .../plugins/sonarqube/tasks/issues_collector.go    |   1 +
 .../plugins/sonarqube/tasks/issues_extractor.go    |   1 +
 .../plugins/sonarqube/tasks/projects_collector.go  |   1 +
 .../plugins/sonarqube/tasks/projects_extractor.go  |   1 +
 backend/plugins/tapd/api/swagger.go                |   2 +-
 backend/plugins/zentao/api/connection.go           |  10 +-
 24 files changed, 291 insertions(+), 52 deletions(-)

diff --git a/backend/generator/template/plugin/tasks/api_collector.go-template b/backend/generator/template/plugin/tasks/api_collector.go-template
index 3626fe4e5..d9eaeec53 100644
--- a/backend/generator/template/plugin/tasks/api_collector.go-template
+++ b/backend/generator/template/plugin/tasks/api_collector.go-template
@@ -71,4 +71,5 @@ var Collect{{ .CollectorDataName }}Meta = plugin.SubTaskMeta{
 	EntryPoint:       Collect{{ .CollectorDataName }},
 	EnabledByDefault: true,
 	Description:      "Collect {{ .CollectorDataName }} data from {{ .PluginName }} api",
+	DomainTypes:      []string{},
 }
diff --git a/backend/generator/template/plugin/tasks/extractor.go-template b/backend/generator/template/plugin/tasks/extractor.go-template
index 73068a5dd..8059c5f37 100644
--- a/backend/generator/template/plugin/tasks/extractor.go-template
+++ b/backend/generator/template/plugin/tasks/extractor.go-template
@@ -52,4 +52,5 @@ var Extract{{ .ExtractorDataName }}Meta = plugin.SubTaskMeta{
 	EntryPoint:       Extract{{ .ExtractorDataName }},
 	EnabledByDefault: true,
 	Description:      "Extract raw data into tool layer table {{ .plugin_name }}_{{ .extractor_data_name }}",
+	DomainTypes:      []string{},
 }
diff --git a/backend/plugins/customize/api/swagger.go b/backend/plugins/customize/api/swagger.go
index 9ade51e2c..81c1c35e5 100644
--- a/backend/plugins/customize/api/swagger.go
+++ b/backend/plugins/customize/api/swagger.go
@@ -36,7 +36,7 @@ type blueprintSetting []struct {
 		Plugin string `json:"plugin" example:"customize"`
 		Scope  []struct {
 			Options tasks.Options `json:"options"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
 
diff --git a/backend/plugins/github/api/swagger.go b/backend/plugins/github/api/swagger.go
index f59a69fc0..90e9ef929 100644
--- a/backend/plugins/github/api/swagger.go
+++ b/backend/plugins/github/api/swagger.go
@@ -70,6 +70,6 @@ type GithubBlueprintSetting []struct {
 				Since string
 			} `json:"options"`
 			Entities []string `json:"entities"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
diff --git a/backend/plugins/gitlab/api/swagger.go b/backend/plugins/gitlab/api/swagger.go
index 03122a77a..d62ee36bc 100644
--- a/backend/plugins/gitlab/api/swagger.go
+++ b/backend/plugins/gitlab/api/swagger.go
@@ -37,7 +37,7 @@ type GitlabBlueprintSetting []struct {
 				Since     string
 			} `json:"options"`
 			Entities []string `json:"entities"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
 
diff --git a/backend/plugins/jenkins/api/connection.go b/backend/plugins/jenkins/api/connection.go
index 3f505ba2c..af9128138 100644
--- a/backend/plugins/jenkins/api/connection.go
+++ b/backend/plugins/jenkins/api/connection.go
@@ -167,7 +167,7 @@ type JenkinsBlueprintSetting []struct {
 			Options struct {
 			} `json:"options"`
 			Entities []string `json:"entities"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
 
diff --git a/backend/plugins/jira/api/swagger.go b/backend/plugins/jira/api/swagger.go
index 2103947c9..6e8965d7c 100644
--- a/backend/plugins/jira/api/swagger.go
+++ b/backend/plugins/jira/api/swagger.go
@@ -41,7 +41,7 @@ type JiraBlueprintSetting []struct {
 				Since   string `json:"since"`
 			} `json:"options"`
 			Entities []string `json:"entities"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
 
diff --git a/backend/plugins/jira/impl/impl.go b/backend/plugins/jira/impl/impl.go
index bf43bf346..34f82dd79 100644
--- a/backend/plugins/jira/impl/impl.go
+++ b/backend/plugins/jira/impl/impl.go
@@ -33,15 +33,17 @@ import (
 	"time"
 )
 
-var _ plugin.PluginMeta = (*Jira)(nil)
-var _ plugin.PluginInit = (*Jira)(nil)
-var _ plugin.PluginTask = (*Jira)(nil)
-var _ plugin.PluginApi = (*Jira)(nil)
-var _ plugin.PluginModel = (*Jira)(nil)
-var _ plugin.PluginMigration = (*Jira)(nil)
-var _ plugin.PluginBlueprintV100 = (*Jira)(nil)
-var _ plugin.CloseablePluginTask = (*Jira)(nil)
-var _ plugin.PluginSource = (*Jira)(nil)
+var _ interface {
+	plugin.PluginMeta
+	plugin.PluginInit
+	plugin.PluginTask
+	plugin.PluginModel
+	plugin.PluginMigration
+	plugin.PluginBlueprintV100
+	plugin.DataSourcePluginBlueprintV200
+	plugin.CloseablePluginTask
+	plugin.PluginSource
+} = (*Jira)(nil)
 
 type Jira struct {
 }
diff --git a/backend/plugins/sonarqube/api/blueprint_v200.go b/backend/plugins/sonarqube/api/blueprint_v200.go
new file mode 100644
index 000000000..2f684c911
--- /dev/null
+++ b/backend/plugins/sonarqube/api/blueprint_v200.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 api
+
+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/didgen"
+	"github.com/apache/incubator-devlake/core/models/domainlayer/securitytesting"
+	"github.com/apache/incubator-devlake/core/plugin"
+	"github.com/apache/incubator-devlake/core/utils"
+	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/sonarqube/models"
+	"time"
+)
+
+func MakeDataSourcePipelinePlanV200(subtaskMetas []plugin.SubTaskMeta, connectionId uint64, bpScopes []*plugin.BlueprintScopeV200, syncPolicy *plugin.BlueprintSyncPolicy) (plugin.PipelinePlan, []plugin.Scope, errors.Error) {
+	plan := make(plugin.PipelinePlan, len(bpScopes))
+	plan, err := makeDataSourcePipelinePlanV200(subtaskMetas, plan, bpScopes, connectionId, syncPolicy)
+	if err != nil {
+		return nil, nil, err
+	}
+	scopes, err := makeScopesV200(bpScopes, connectionId)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return plan, scopes, nil
+}
+
+func makeDataSourcePipelinePlanV200(
+	subtaskMetas []plugin.SubTaskMeta,
+	plan plugin.PipelinePlan,
+	bpScopes []*plugin.BlueprintScopeV200,
+	connectionId uint64,
+	syncPolicy *plugin.BlueprintSyncPolicy,
+) (plugin.PipelinePlan, errors.Error) {
+	for i, bpScope := range bpScopes {
+		stage := plan[i]
+		if stage == nil {
+			stage = plugin.PipelineStage{}
+		}
+		// construct task options for Sonarqube
+		options := make(map[string]interface{})
+		options["connectionId"] = connectionId
+		options["projectKey"] = bpScope.Id
+		if syncPolicy.CreatedDateAfter != nil {
+			options["createdDateAfter"] = syncPolicy.CreatedDateAfter.Format(time.RFC3339)
+		}
+
+		subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, bpScope.Entities)
+		if err != nil {
+			return nil, err
+		}
+		stage = append(stage, &plugin.PipelineTask{
+			Plugin:   "sonarqube",
+			Subtasks: subtasks,
+			Options:  options,
+		})
+		plan[i] = stage
+	}
+
+	return plan, nil
+}
+
+func makeScopesV200(bpScopes []*plugin.BlueprintScopeV200, connectionId uint64) ([]plugin.Scope, errors.Error) {
+	scopes := make([]plugin.Scope, 0)
+	for _, bpScope := range bpScopes {
+		sonarqubeProject := &models.SonarqubeProject{}
+		// get repo from db
+		err := basicRes.GetDal().First(sonarqubeProject,
+			dal.Where("connection_id = ? and `key` = ?",
+				connectionId, bpScope.Id))
+		if err != nil {
+			return nil, errors.Default.Wrap(err, fmt.Sprintf("fail to find sonarqube project %s", bpScope.Id))
+		}
+		// add board to scopes
+		if utils.StringsContains(bpScope.Entities, plugin.DOMAIN_TYPE_SECURITY_TESTING) {
+			stProject := &securitytesting.StProject{
+				DomainEntity: domainlayer.DomainEntity{
+					Id: didgen.NewDomainIdGenerator(&models.SonarqubeProject{}).Generate(sonarqubeProject.ConnectionId, sonarqubeProject.Key),
+				},
+				Name: sonarqubeProject.Name,
+			}
+			scopes = append(scopes, stProject)
+		}
+	}
+	return scopes, nil
+}
diff --git a/backend/plugins/sonarqube/api/blueprint_v200_test.go b/backend/plugins/sonarqube/api/blueprint_v200_test.go
new file mode 100644
index 000000000..ccfbe6bde
--- /dev/null
+++ b/backend/plugins/sonarqube/api/blueprint_v200_test.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 api
+
+import (
+	"github.com/apache/incubator-devlake/core/models/domainlayer"
+	"github.com/apache/incubator-devlake/core/models/domainlayer/securitytesting"
+	"github.com/apache/incubator-devlake/core/plugin"
+	mockcontext "github.com/apache/incubator-devlake/mocks/core/context"
+	mockdal "github.com/apache/incubator-devlake/mocks/core/dal"
+	mockplugin "github.com/apache/incubator-devlake/mocks/core/plugin"
+	"github.com/apache/incubator-devlake/plugins/sonarqube/models"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"testing"
+)
+
+func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
+	mockMeta := mockplugin.NewPluginMeta(t)
+	mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/sonarqube")
+	err := plugin.RegisterPlugin("sonarqube", mockMeta)
+	assert.Nil(t, err)
+	bs := &plugin.BlueprintScopeV200{
+		Entities: []string{"SECURITYTESTING"},
+		Id:       "f5a50c63-2e8f-4107-9014-853f6f467757",
+	}
+	syncPolicy := &plugin.BlueprintSyncPolicy{}
+	bpScopes := make([]*plugin.BlueprintScopeV200, 0)
+	bpScopes = append(bpScopes, bs)
+	plan := make(plugin.PipelinePlan, len(bpScopes))
+	plan, err = makeDataSourcePipelinePlanV200(nil, plan, bpScopes, uint64(1), syncPolicy)
+	assert.Nil(t, err)
+	basicRes = NewMockBasicRes()
+	scopes, err := makeScopesV200(bpScopes, uint64(1))
+	assert.Nil(t, err)
+
+	expectPlan := plugin.PipelinePlan{
+		plugin.PipelineStage{
+			{
+				Plugin:   "sonarqube",
+				Subtasks: []string{},
+				Options: map[string]interface{}{
+					"connectionId": uint64(1),
+					"projectKey":   "f5a50c63-2e8f-4107-9014-853f6f467757",
+				},
+			},
+		},
+	}
+	assert.Equal(t, expectPlan, plan)
+
+	expectScopes := make([]plugin.Scope, 0)
+	sonarqubeProject := &securitytesting.StProject{
+		DomainEntity: domainlayer.DomainEntity{
+			Id: "sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757",
+		},
+		Name: "aerosolve",
+	}
+
+	expectScopes = append(expectScopes, sonarqubeProject)
+	assert.Equal(t, expectScopes, scopes)
+}
+
+// NewMockBasicRes FIXME ...
+func NewMockBasicRes() *mockcontext.BasicRes {
+	sonarqubeProject := &models.SonarqubeProject{
+		ConnectionId: 1,
+		Key:          "f5a50c63-2e8f-4107-9014-853f6f467757",
+		Name:         "aerosolve",
+	}
+
+	mockRes := new(mockcontext.BasicRes)
+	mockDal := new(mockdal.Dal)
+
+	mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+		dst := args.Get(0).(*models.SonarqubeProject)
+		*dst = *sonarqubeProject
+	}).Return(nil).Once()
+
+	mockRes.On("GetDal").Return(mockDal)
+	mockRes.On("GetConfig", mock.Anything).Return("")
+
+	return mockRes
+}
diff --git a/backend/plugins/jira/api/swagger.go b/backend/plugins/sonarqube/api/swagger.go
similarity index 55%
copy from backend/plugins/jira/api/swagger.go
copy to backend/plugins/sonarqube/api/swagger.go
index 2103947c9..4164e0fbd 100644
--- a/backend/plugins/jira/api/swagger.go
+++ b/backend/plugins/sonarqube/api/swagger.go
@@ -17,48 +17,41 @@ limitations under the License.
 
 package api
 
-import (
-	"github.com/apache/incubator-devlake/plugins/jira/tasks"
-)
-
-// @Summary blueprints setting for jira
-// @Description blueprint setting for jira
-// @Tags plugins/jira
+// @Summary blueprints setting for sonarqube
+// @Description blueprint setting for sonarqube
+// @Tags plugins/sonarqube
 // @Accept application/json
-// @Param blueprint-setting body JiraBlueprintSetting true "json"
-// @Router /blueprints/jira/blueprint-setting [post]
+// @Param blueprint-setting body SonarqubeBlueprintSetting true "json"
+// @Router /blueprints/sonarqube/blueprint-setting [post]
 func _() {}
 
-type JiraBlueprintSetting []struct {
+type SonarqubeBlueprintSetting []struct {
 	Version     string `json:"version"`
 	Connections []struct {
 		Plugin       string `json:"plugin"`
 		ConnectionID int    `json:"connectionId"`
 		Scope        []struct {
-			Transformation tasks.JiraTransformationRule `json:"transformation"`
-			Options        struct {
-				BoardId uint64 `json:"boardId"`
-				Since   string `json:"since"`
+			Options struct {
+				Id int `json:"id"`
 			} `json:"options"`
 			Entities []string `json:"entities"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
 
-// @Summary pipelines plan for jira
-// @Description pipelines plan for jira
-// @Tags plugins/jira
+// @Summary pipelines plan for sonarqube
+// @Description pipelines plan for sonarqube
+// @Tags plugins/sonarqube
 // @Accept application/json
-// @Param pipeline-plan body JiraPipelinePlan true "json"
-// @Router /pipelines/jira/pipeline-plan [post]
+// @Param pipeline-plan body SonarqubePipelinePlan true "json"
+// @Router /pipelines/sonarqube/pipeline-plan [post]
 func _() {}
 
-type JiraPipelinePlan [][]struct {
+type SonarqubePipelinePlan [][]struct {
 	Plugin   string   `json:"plugin"`
 	Subtasks []string `json:"subtasks"`
 	Options  struct {
-		BoardID             int                          `json:"boardId"`
-		ConnectionID        int                          `json:"connectionId"`
-		TransformationRules tasks.JiraTransformationRule `json:"transformationRules"`
+		ProjectKey   string `json:"projectKey"`
+		ConnectionID int    `json:"connectionId"`
 	} `json:"options"`
 }
diff --git a/backend/plugins/sonarqube/impl/impl.go b/backend/plugins/sonarqube/impl/impl.go
index 58f44d901..38358d462 100644
--- a/backend/plugins/sonarqube/impl/impl.go
+++ b/backend/plugins/sonarqube/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/core/dal"
 	"time"
 
 	"github.com/apache/incubator-devlake/core/context"
@@ -33,12 +34,15 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Sonarqube)(nil)
-var _ plugin.PluginInit = (*Sonarqube)(nil)
-var _ plugin.PluginTask = (*Sonarqube)(nil)
-var _ plugin.PluginApi = (*Sonarqube)(nil)
-var _ plugin.PluginBlueprintV100 = (*Sonarqube)(nil)
-var _ plugin.CloseablePluginTask = (*Sonarqube)(nil)
+var _ interface {
+	plugin.PluginMeta
+	plugin.PluginInit
+	plugin.PluginTask
+	plugin.PluginMigration
+	plugin.DataSourcePluginBlueprintV200
+	plugin.CloseablePluginTask
+	plugin.PluginSource
+} = (*Sonarqube)(nil)
 
 type Sonarqube struct{}
 
@@ -51,6 +55,30 @@ func (p Sonarqube) Init(br context.BasicRes) errors.Error {
 	return nil
 }
 
+func (p Sonarqube) Connection() interface{} {
+	return &models.SonarqubeConnection{}
+}
+
+func (p Sonarqube) Scope() interface{} {
+	return &models.SonarqubeProject{}
+}
+
+func (p Sonarqube) TransformationRule() interface{} {
+	return nil
+}
+
+func (p Sonarqube) GetTablesInfo() []dal.Tabler {
+	return []dal.Tabler{
+		&models.SonarqubeConnection{},
+		&models.SonarqubeProject{},
+		&models.SonarqubeIssue{},
+		&models.SonarqubeIssueCodeBlock{},
+		&models.SonarqubeHotspot{},
+		&models.SonarqubeFileMetrics{},
+		&models.SonarqubeAccount{},
+	}
+}
+
 func (p Sonarqube) SubTaskMetas() []plugin.SubTaskMeta {
 	return []plugin.SubTaskMeta{
 		tasks.CollectProjectsMeta,
@@ -136,8 +164,8 @@ func (p Sonarqube) ApiResources() map[string]map[string]plugin.ApiResourceHandle
 	}
 }
 
-func (p Sonarqube) MakePipelinePlan(connectionId uint64, scope []*plugin.BlueprintScopeV100) (plugin.PipelinePlan, errors.Error) {
-	return api.MakePipelinePlan(p.SubTaskMetas(), connectionId, scope)
+func (p Sonarqube) MakeDataSourcePipelinePlanV200(connectionId uint64, scopes []*plugin.BlueprintScopeV200, syncPolicy plugin.BlueprintSyncPolicy) (pp plugin.PipelinePlan, sc []plugin.Scope, err errors.Error) {
+	return api.MakeDataSourcePipelinePlanV200(p.SubTaskMetas(), connectionId, scopes, &syncPolicy)
 }
 
 func (p Sonarqube) Close(taskCtx plugin.TaskContext) errors.Error {
diff --git a/backend/plugins/sonarqube/tasks/accounts_collector.go b/backend/plugins/sonarqube/tasks/accounts_collector.go
index 24aeff1a4..29842ac7c 100644
--- a/backend/plugins/sonarqube/tasks/accounts_collector.go
+++ b/backend/plugins/sonarqube/tasks/accounts_collector.go
@@ -72,4 +72,5 @@ var CollectAccountsMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectAccounts,
 	EnabledByDefault: true,
 	Description:      "Collect Accounts data from Sonarqube user api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
 }
diff --git a/backend/plugins/sonarqube/tasks/accounts_extractor.go b/backend/plugins/sonarqube/tasks/accounts_extractor.go
index bee692751..1d139b383 100644
--- a/backend/plugins/sonarqube/tasks/accounts_extractor.go
+++ b/backend/plugins/sonarqube/tasks/accounts_extractor.go
@@ -54,4 +54,5 @@ var ExtractAccountsMeta = plugin.SubTaskMeta{
 	EntryPoint:       ExtractAccounts,
 	EnabledByDefault: true,
 	Description:      "Extract raw data into tool layer table sonarqube_accounts",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
 }
diff --git a/backend/plugins/sonarqube/tasks/filemetrics_collector.go b/backend/plugins/sonarqube/tasks/filemetrics_collector.go
index bf70a0b42..4f4f5282b 100644
--- a/backend/plugins/sonarqube/tasks/filemetrics_collector.go
+++ b/backend/plugins/sonarqube/tasks/filemetrics_collector.go
@@ -72,4 +72,5 @@ var CollectFilemetricsMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectFilemetrics,
 	EnabledByDefault: true,
 	Description:      "Collect Filemetrics data from Sonarqube api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
diff --git a/backend/plugins/sonarqube/tasks/filemetrics_extractor.go b/backend/plugins/sonarqube/tasks/filemetrics_extractor.go
index 3ff73f8ee..6b70b399f 100644
--- a/backend/plugins/sonarqube/tasks/filemetrics_extractor.go
+++ b/backend/plugins/sonarqube/tasks/filemetrics_extractor.go
@@ -136,6 +136,7 @@ var ExtractFilemetricsMeta = plugin.SubTaskMeta{
 	EntryPoint:       ExtractFilemetrics,
 	EnabledByDefault: true,
 	Description:      "Extract raw data into tool layer table sonarqube_filemetrics",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
 
 type fileMetricsResponse struct {
diff --git a/backend/plugins/sonarqube/tasks/hotspots_collector.go b/backend/plugins/sonarqube/tasks/hotspots_collector.go
index 6d733f135..97ac7d7a7 100644
--- a/backend/plugins/sonarqube/tasks/hotspots_collector.go
+++ b/backend/plugins/sonarqube/tasks/hotspots_collector.go
@@ -71,4 +71,5 @@ var CollectHotspotsMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectHotspots,
 	EnabledByDefault: true,
 	Description:      "Collect Hotspots data from Sonarqube api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
diff --git a/backend/plugins/sonarqube/tasks/hotspots_extractor.go b/backend/plugins/sonarqube/tasks/hotspots_extractor.go
index 0ba42f744..0c394cbc2 100644
--- a/backend/plugins/sonarqube/tasks/hotspots_extractor.go
+++ b/backend/plugins/sonarqube/tasks/hotspots_extractor.go
@@ -55,4 +55,5 @@ var ExtractHotspotsMeta = plugin.SubTaskMeta{
 	EntryPoint:       ExtractHotspots,
 	EnabledByDefault: true,
 	Description:      "Extract raw data into tool layer table sonarqube_hotspots",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
diff --git a/backend/plugins/sonarqube/tasks/issues_collector.go b/backend/plugins/sonarqube/tasks/issues_collector.go
index bd1b85755..57a785542 100644
--- a/backend/plugins/sonarqube/tasks/issues_collector.go
+++ b/backend/plugins/sonarqube/tasks/issues_collector.go
@@ -77,4 +77,5 @@ var CollectIssuesMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectIssues,
 	EnabledByDefault: true,
 	Description:      "Collect issues data from Sonarqube api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
diff --git a/backend/plugins/sonarqube/tasks/issues_extractor.go b/backend/plugins/sonarqube/tasks/issues_extractor.go
index 97e941797..d659c0117 100644
--- a/backend/plugins/sonarqube/tasks/issues_extractor.go
+++ b/backend/plugins/sonarqube/tasks/issues_extractor.go
@@ -108,6 +108,7 @@ var ExtractIssuesMeta = plugin.SubTaskMeta{
 	EntryPoint:       ExtractIssues,
 	EnabledByDefault: true,
 	Description:      "Extract raw data into tool layer table sonarqube_issues",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
 
 type IssuesResponse struct {
diff --git a/backend/plugins/sonarqube/tasks/projects_collector.go b/backend/plugins/sonarqube/tasks/projects_collector.go
index da8bd87b4..422e89ebf 100644
--- a/backend/plugins/sonarqube/tasks/projects_collector.go
+++ b/backend/plugins/sonarqube/tasks/projects_collector.go
@@ -78,4 +78,5 @@ var CollectProjectsMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectProjects,
 	EnabledByDefault: true,
 	Description:      "Collect Projects data from Sonarqube api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
diff --git a/backend/plugins/sonarqube/tasks/projects_extractor.go b/backend/plugins/sonarqube/tasks/projects_extractor.go
index a07c7d7bf..3fec3ffbe 100644
--- a/backend/plugins/sonarqube/tasks/projects_extractor.go
+++ b/backend/plugins/sonarqube/tasks/projects_extractor.go
@@ -53,4 +53,5 @@ var ExtractProjectsMeta = plugin.SubTaskMeta{
 	EntryPoint:       ExtractProjects,
 	EnabledByDefault: true,
 	Description:      "Extract raw data into tool layer table sonarqube_projects",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_SECURITY_TESTING},
 }
diff --git a/backend/plugins/tapd/api/swagger.go b/backend/plugins/tapd/api/swagger.go
index 573550a93..f5782bf8e 100644
--- a/backend/plugins/tapd/api/swagger.go
+++ b/backend/plugins/tapd/api/swagger.go
@@ -39,7 +39,7 @@ type TapdBlueprintSetting []struct {
 				TransformationRules TransformationRules `json:"transformationRules"`
 			} `json:"options"`
 			Entities []string `json:"entities"`
-		} `json:"scope"`
+		} `json:"scopes"`
 	} `json:"connections"`
 }
 
diff --git a/backend/plugins/zentao/api/connection.go b/backend/plugins/zentao/api/connection.go
index 177011891..60a9f2169 100644
--- a/backend/plugins/zentao/api/connection.go
+++ b/backend/plugins/zentao/api/connection.go
@@ -52,7 +52,7 @@ func TestConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 
 // @Summary create zentao connection
 // @Description Create zentao connection
-// @Tags pluginszentao/
+// @Tags plugins/zentao/
 // @Param body body models.ZentaoConnection true "json body"
 // @Success 200  {object} models.ZentaoConnection
 // @Failure 400  {string} errcode.Error "Bad Request"
@@ -70,7 +70,7 @@ func PostConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 
 // @Summary patch zentao connection
 // @Description Patch zentao connection
-// @Tags pluginszentao/
+// @Tags plugins/zentao/
 // @Param body body models.ZentaoConnection true "json body"
 // @Success 200  {object} models.ZentaoConnection
 // @Failure 400  {string} errcode.Error "Bad Request"
@@ -87,7 +87,7 @@ func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 
 // @Summary delete a zentao connection
 // @Description Delete a zentao connection
-// @Tags pluginszentao/
+// @Tags plugins/zentao/
 // @Success 200  {object} models.ZentaoConnection
 // @Failure 400  {string} errcode.Error "Bad Request"
 // @Failure 500  {string} errcode.Error "Internal Error"
@@ -104,7 +104,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput
 
 // @Summary get all zentao connections
 // @Description Get all zentao connections
-// @Tags pluginszentao/
+// @Tags plugins/zentao/
 // @Success 200  {object} []models.ZentaoConnection
 // @Failure 400  {string} errcode.Error "Bad Request"
 // @Failure 500  {string} errcode.Error "Internal Error"
@@ -120,7 +120,7 @@ func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 
 // @Summary get zentao connection detail
 // @Description Get zentao connection detail
-// @Tags pluginszentao/
+// @Tags plugins/zentao/
 // @Success 200  {object} models.ZentaoConnection
 // @Failure 400  {string} errcode.Error "Bad Request"
 // @Failure 500  {string} errcode.Error "Internal Error"