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

hez 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 4763e35df [feat-5513]: Scope check before deleting connections + 
various small fixes (#5515)
4763e35df is described below

commit 4763e35df650e45099bbf9f1226f619a459bd801
Author: Keon Amini <[email protected]>
AuthorDate: Mon Jun 19 14:54:50 2023 -0500

    [feat-5513]: Scope check before deleting connections + various small fixes 
(#5515)
    
    * refactor: connection helper now manages plugin-name
    refactor: most plugins now implements PluginSource
    feat: connection helper now uses PluginSource impl to refer to scopes
    fix: removed caching of table in scope_helper due to a race-condition bug 
encountered by e2e test
    fix: removed pagerduty's scope_config leftover references
    
    * fix: Changed Scopes() method to return ToolLayerScope instead - this 
fixed a bug from PagerDuty, and makes the code less error-prone
    
    * fix: minor fixes to python reflection logic + test added
    
    * refactor: Remove Zentao Products from scopes list per issue #5523. 
Consequently, redefine PluginSource interface to just return a single Scope
---
 backend/core/models/dynamic_tabler.go              | 49 ++++++++++--------
 backend/core/plugin/hub_test.go                    |  8 +++
 backend/core/plugin/plugin_meta.go                 | 14 ++---
 backend/core/utils/json_test.go                    | 12 ++---
 .../helpers/pluginhelper/api/connection_helper.go  | 27 ++++++++--
 backend/helpers/pluginhelper/api/misc_helpers.go   |  2 +-
 .../pluginhelper/api/scope_generic_helper.go       | 27 +++-------
 .../helpers/pluginhelper/api/scope_helper_test.go  |  2 +-
 backend/plugins/ae/api/connection.go               |  2 +-
 backend/plugins/ae/api/init.go                     |  5 +-
 backend/plugins/ae/impl/impl.go                    | 37 +++++++++++---
 backend/plugins/bamboo/api/blueprint_V200_test.go  |  3 +-
 backend/plugins/bamboo/api/connection.go           |  2 +-
 backend/plugins/bamboo/api/init.go                 |  5 +-
 backend/plugins/bamboo/impl/impl.go                | 37 ++++++++------
 .../plugins/bitbucket/api/blueprint_V200_test.go   | 10 ++--
 backend/plugins/bitbucket/api/connection.go        |  2 +-
 backend/plugins/bitbucket/api/init.go              |  5 +-
 backend/plugins/bitbucket/impl/impl.go             | 35 ++++++++-----
 backend/plugins/customize/impl/impl.go             | 16 ++++--
 backend/plugins/dbt/impl/impl.go                   | 14 +++--
 backend/plugins/dora/impl/impl.go                  | 18 ++++---
 backend/plugins/feishu/api/connection.go           |  2 +-
 backend/plugins/feishu/api/init.go                 |  4 +-
 backend/plugins/feishu/impl/impl.go                | 37 +++++++++++---
 backend/plugins/gitee/api/connection.go            |  2 +-
 backend/plugins/gitee/api/init.go                  |  5 +-
 backend/plugins/gitee/impl/impl.go                 | 42 +++++++++++----
 backend/plugins/gitee/models/repo.go               | 12 +++++
 backend/plugins/gitextractor/impl/impl.go          | 12 +++--
 backend/plugins/github/api/blueprint_V200_test.go  | 10 ++--
 backend/plugins/github/api/connection.go           |  2 +-
 backend/plugins/github/api/init.go                 |  4 +-
 backend/plugins/github/impl/impl.go                | 33 +++++++-----
 backend/plugins/github_graphql/impl/impl.go        | 26 ++++++----
 backend/plugins/gitlab/api/blueprint_V200_test.go  |  3 +-
 backend/plugins/gitlab/api/connection.go           |  2 +-
 backend/plugins/gitlab/api/init.go                 |  5 +-
 backend/plugins/gitlab/impl/impl.go                | 16 ++++--
 backend/plugins/icla/impl/impl.go                  | 21 +++++---
 backend/plugins/jenkins/api/blueprint_v200_test.go | 10 ++--
 backend/plugins/jenkins/api/connection.go          |  2 +-
 backend/plugins/jenkins/api/init.go                |  5 +-
 backend/plugins/jenkins/impl/impl.go               | 35 ++++++++-----
 backend/plugins/jira/api/blueprint_v200_test.go    | 10 ++--
 backend/plugins/jira/api/connection.go             |  2 +-
 backend/plugins/jira/api/init.go                   |  5 +-
 backend/plugins/jira/impl/impl.go                  | 16 ++++--
 backend/plugins/org/impl/impl.go                   | 16 ++++--
 backend/plugins/pagerduty/api/connection.go        |  2 +-
 backend/plugins/pagerduty/api/init.go              |  5 +-
 backend/plugins/pagerduty/e2e/incident_test.go     |  7 +--
 backend/plugins/pagerduty/impl/impl.go             | 45 ++++++++++++-----
 backend/plugins/pagerduty/models/scope_config.go   |  4 --
 backend/plugins/pagerduty/models/service.go        | 13 ++++-
 backend/plugins/refdiff/impl/impl.go               | 16 ++++--
 backend/plugins/slack/api/connection.go            |  2 +-
 backend/plugins/slack/api/init.go                  |  5 +-
 backend/plugins/slack/impl/impl.go                 | 37 +++++++++++---
 .../plugins/sonarqube/api/blueprint_v200_test.go   |  2 +
 backend/plugins/sonarqube/api/connection.go        |  2 +-
 backend/plugins/sonarqube/api/init.go              |  5 +-
 backend/plugins/sonarqube/impl/impl.go             | 16 ++++--
 backend/plugins/starrocks/impl/impl.go             | 12 +++--
 backend/plugins/tapd/api/blueprint_v200_test.go    | 10 ++--
 backend/plugins/tapd/api/connection.go             |  2 +-
 backend/plugins/tapd/api/init.go                   |  5 +-
 backend/plugins/tapd/impl/impl.go                  | 37 +++++++++++---
 backend/plugins/tapd/tasks/shared_test.go          |  1 +
 backend/plugins/teambition/api/connection.go       |  2 +-
 backend/plugins/teambition/api/init.go             |  5 +-
 backend/plugins/teambition/impl/impl.go            | 34 ++++++++++---
 backend/plugins/trello/api/connection.go           |  2 +-
 backend/plugins/trello/api/init.go                 |  4 +-
 backend/plugins/trello/impl/impl.go                | 16 ++++--
 backend/plugins/trello/models/board.go             | 11 ++++
 backend/plugins/webhook/api/connection.go          |  2 +-
 backend/plugins/webhook/api/init.go                |  5 +-
 backend/plugins/webhook/impl/impl.go               | 19 ++++---
 backend/plugins/zentao/api/blueprint_V200_test.go  | 10 ++--
 backend/plugins/zentao/api/blueprint_v200.go       |  2 -
 backend/plugins/zentao/api/connection.go           |  2 +-
 backend/plugins/zentao/api/init.go                 |  5 +-
 backend/plugins/zentao/impl/impl.go                | 59 +++++++++++++++++++---
 .../server/services/remote/models/conversion.go    |  2 +-
 backend/server/services/remote/models/models.go    | 23 ++++++++-
 .../server/services/remote/models/plugin_remote.go |  1 +
 .../services/remote/plugin/connection_api.go       | 12 ++---
 .../server/services/remote/plugin/default_api.go   | 29 +++++------
 backend/server/services/remote/plugin/init.go      | 11 +---
 .../services/remote/plugin/plugin_extensions.go    |  2 +-
 .../server/services/remote/plugin/plugin_impl.go   | 39 +++++++++++---
 .../services/remote/plugin/remote_scope_api.go     |  2 +-
 backend/server/services/remote/plugin/scope_api.go | 10 ++--
 .../services/remote/plugin/scope_db_helper.go      |  2 +-
 backend/test/e2e/remote/python_plugin_test.go      | 24 ++++++---
 backend/test/helper/api.go                         |  6 +--
 97 files changed, 865 insertions(+), 375 deletions(-)

diff --git a/backend/core/models/dynamic_tabler.go 
b/backend/core/models/dynamic_tabler.go
index 9c3c5cf8a..d8dea00e6 100644
--- a/backend/core/models/dynamic_tabler.go
+++ b/backend/core/models/dynamic_tabler.go
@@ -29,41 +29,54 @@ import (
 // DynamicTabler is a core.Tabler that wraps a runtime (anonymously) generated 
data-model. Due to limitations of
 // reflection in Go and the GORM framework, the underlying model and the table 
have to be explicitly passed into dal.Dal's API
 // via Unwrap() and TableName()
-type DynamicTabler struct {
+type DynamicTabler interface {
+       dal.Tabler
+       NewValue() any
+       New() DynamicTabler
+       NewSlice() DynamicTabler
+       From(src any) errors.Error
+       To(target any) errors.Error
+       Unwrap() any
+       UnwrapPtr() *any
+       UnwrapSlice() []any
+}
+
+// DynamicTablerImpl the implementation of DynamicTabler
+type DynamicTablerImpl struct {
        objType reflect.Type
        wrapped any
        table   string
 }
 
-func NewDynamicTabler(tableName string, objType reflect.Type) *DynamicTabler {
-       return &DynamicTabler{
+func NewDynamicTabler(tableName string, objType reflect.Type) DynamicTabler {
+       return &DynamicTablerImpl{
                objType: objType,
                table:   tableName,
        }
 }
 
-func (d *DynamicTabler) NewValue() any {
+func (d *DynamicTablerImpl) NewValue() any {
        return reflect.New(d.objType).Interface()
 }
 
-func (d *DynamicTabler) New() *DynamicTabler {
-       return &DynamicTabler{
+func (d *DynamicTablerImpl) New() DynamicTabler {
+       return &DynamicTablerImpl{
                objType: d.objType,
                wrapped: d.NewValue(),
                table:   d.table,
        }
 }
 
-func (d *DynamicTabler) NewSlice() *DynamicTabler {
+func (d *DynamicTablerImpl) NewSlice() DynamicTabler {
        sliceType := reflect.SliceOf(d.objType)
-       return &DynamicTabler{
+       return &DynamicTablerImpl{
                objType: sliceType,
                wrapped: reflect.New(sliceType).Interface(),
                table:   d.table,
        }
 }
 
-func (d *DynamicTabler) From(src any) errors.Error {
+func (d *DynamicTablerImpl) From(src any) errors.Error {
        b, err := json.Marshal(src)
        if err != nil {
                return errors.Convert(err)
@@ -71,7 +84,7 @@ func (d *DynamicTabler) From(src any) errors.Error {
        return errors.Convert(json.Unmarshal(b, d.wrapped))
 }
 
-func (d *DynamicTabler) To(target any) errors.Error {
+func (d *DynamicTablerImpl) To(target any) errors.Error {
        b, err := json.Marshal(d.wrapped)
        if err != nil {
                return errors.Convert(err)
@@ -79,19 +92,15 @@ func (d *DynamicTabler) To(target any) errors.Error {
        return errors.Convert(json.Unmarshal(b, target))
 }
 
-func (d *DynamicTabler) Set(x any) {
-       d.wrapped = x
-}
-
-func (d *DynamicTabler) Unwrap() any {
+func (d *DynamicTablerImpl) Unwrap() any {
        return d.wrapped
 }
 
-func (d *DynamicTabler) UnwrapPtr() *any {
+func (d *DynamicTablerImpl) UnwrapPtr() *any {
        return &d.wrapped
 }
 
-func (d *DynamicTabler) UnwrapSlice() []any {
+func (d *DynamicTablerImpl) UnwrapSlice() []any {
        var arr []any
        slice := reflect.ValueOf(d.wrapped).Elem()
        for i := 0; i < slice.Len(); i++ {
@@ -100,15 +109,15 @@ func (d *DynamicTabler) UnwrapSlice() []any {
        return arr
 }
 
-func (d *DynamicTabler) TableName() string {
+func (d *DynamicTablerImpl) TableName() string {
        return d.table
 }
 
-var _ dal.Tabler = (*DynamicTabler)(nil)
+var _ DynamicTabler = (*DynamicTablerImpl)(nil)
 
 // UnwrapObject if the actual object is wrapped in some proxy, it unwinds and 
returns it, otherwise this is idempotent
 func UnwrapObject(ifc any) any {
-       if dynamic, ok := ifc.(*DynamicTabler); ok {
+       if dynamic, ok := ifc.(DynamicTabler); ok {
                return dynamic.Unwrap()
        }
        return ifc
diff --git a/backend/core/plugin/hub_test.go b/backend/core/plugin/hub_test.go
index ae8a3e95b..c8864a55e 100644
--- a/backend/core/plugin/hub_test.go
+++ b/backend/core/plugin/hub_test.go
@@ -28,6 +28,10 @@ var _ PluginMeta = (*Bar)(nil)
 
 type Foo string
 
+func (f *Foo) Name() string {
+       return "foo"
+}
+
 func (f *Foo) Description() string {
        return "foo"
 }
@@ -38,6 +42,10 @@ func (f *Foo) RootPkgPath() string {
 
 type Bar string
 
+func (b *Bar) Name() string {
+       return "bar"
+}
+
 func (b *Bar) Description() string {
        return "foo"
 }
diff --git a/backend/core/plugin/plugin_meta.go 
b/backend/core/plugin/plugin_meta.go
index a261ac9b4..d300e1f52 100644
--- a/backend/core/plugin/plugin_meta.go
+++ b/backend/core/plugin/plugin_meta.go
@@ -17,11 +17,14 @@ limitations under the License.
 
 package plugin
 
+import "github.com/apache/incubator-devlake/core/dal"
+
 // PluginMeta is the Minimal features a plugin should comply, should be 
implemented by all plugins
 type PluginMeta interface {
        Description() string
        // PkgPath information lost when compiled as plugin(.so)
        RootPkgPath() string
+       Name() string
 }
 
 type GrafanaDashboard struct {
@@ -42,9 +45,8 @@ type PluginIcon interface {
 }
 
 // PluginSource abstracts data sources
-// type PluginSource interface {
-//     Connection() interface{}
-//     Scope() interface{}
-// Deprecated: rename to ScopeConfig
-// ScopeConfig() interface{}
-// }
+type PluginSource interface {
+       Connection() dal.Tabler
+       Scope() ToolLayerScope
+       ScopeConfig() dal.Tabler
+}
diff --git a/backend/core/utils/json_test.go b/backend/core/utils/json_test.go
index ae573b932..13973223a 100644
--- a/backend/core/utils/json_test.go
+++ b/backend/core/utils/json_test.go
@@ -42,7 +42,7 @@ func TestMissingProperty(t *testing.T) {
        _, err := GetProperty[int](object, "name")
 
        assert.Error(t, err)
-       assert.Equal(t, "Missing property \"name\"", err.Error())
+       assert.Contains(t, err.Error(), "Missing property \"name\"")
 }
 
 func TestInvalidPropertyType(t *testing.T) {
@@ -53,7 +53,7 @@ func TestInvalidPropertyType(t *testing.T) {
        _, err := GetProperty[string](object, "id")
 
        assert.Error(t, err)
-       assert.Equal(t, "Value is not of type string", err.Error())
+       assert.Contains(t, err.Error(), "Value is not of type string")
 }
 
 func TestGetItemInRange(t *testing.T) {
@@ -71,7 +71,7 @@ func TestGetItemOutOfRange(t *testing.T) {
        _, err := GetItem[int](array, 3)
 
        assert.Error(t, err)
-       assert.Equal(t, "Index 3 out of range", err.Error())
+       assert.Contains(t, err.Error(), "Index 3 out of range")
 }
 
 func TestConvertSlice(t *testing.T) {
@@ -89,7 +89,7 @@ func TestConvertSliceInvalidType(t *testing.T) {
        val, err := Convert[[]string](value)
        _ = val
        assert.Error(t, err)
-       assert.Equal(t, "Element 0 is not of type string", err.Error())
+       assert.Contains(t, err.Error(), "Element 0 is not of type string")
 }
 
 func TestConvertSliceInvalidValue(t *testing.T) {
@@ -98,7 +98,7 @@ func TestConvertSliceInvalidValue(t *testing.T) {
        _, err := Convert[[]int](value)
 
        assert.Error(t, err)
-       assert.Equal(t, "Element 1 is not of type int", err.Error())
+       assert.Contains(t, err.Error(), "Element 1 is not of type int")
 }
 
 func TestConvertSliceInvalidSlice(t *testing.T) {
@@ -107,5 +107,5 @@ func TestConvertSliceInvalidSlice(t *testing.T) {
        _, err := Convert[[]int](value)
 
        assert.Error(t, err)
-       assert.Equal(t, "Value is not a slice", err.Error())
+       assert.Contains(t, err.Error(), "Value is not a slice")
 }
diff --git a/backend/helpers/pluginhelper/api/connection_helper.go 
b/backend/helpers/pluginhelper/api/connection_helper.go
index fd2d4c205..70d9dfada 100644
--- a/backend/helpers/pluginhelper/api/connection_helper.go
+++ b/backend/helpers/pluginhelper/api/connection_helper.go
@@ -18,6 +18,7 @@ limitations under the License.
 package api
 
 import (
+       "fmt"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/services"
        "strconv"
 
@@ -37,12 +38,14 @@ type ConnectionApiHelper struct {
        db               dal.Dal
        validator        *validator.Validate
        bpManager        *services.BlueprintManager
+       pluginName       string
 }
 
 // NewConnectionHelper creates a ConnectionHelper for connection management
 func NewConnectionHelper(
        basicRes context.BasicRes,
        vld *validator.Validate,
+       pluginName string,
 ) *ConnectionApiHelper {
        if vld == nil {
                vld = validator.New()
@@ -53,6 +56,7 @@ func NewConnectionHelper(
                db:               basicRes.GetDal(),
                validator:        vld,
                bpManager:        
services.NewBlueprintManager(basicRes.GetDal()),
+               pluginName:       pluginName,
        }
 }
 
@@ -104,14 +108,22 @@ func (c *ConnectionApiHelper) List(connections 
interface{}) errors.Error {
 }
 
 // Delete connection
-func (c *ConnectionApiHelper) Delete(plugin string, connection interface{}) 
(*services.BlueprintProjectPairs, errors.Error) {
+func (c *ConnectionApiHelper) Delete(connection interface{}) 
(*services.BlueprintProjectPairs, errors.Error) {
        connectionId := reflectField(connection, "ID").Uint()
-       referencingBps, err := c.bpManager.GetBlueprintsByConnection(plugin, 
connectionId)
+       referencingBps, err := 
c.bpManager.GetBlueprintsByConnection(c.pluginName, connectionId)
        if err != nil {
                return nil, err
        }
        if len(referencingBps) > 0 {
-               return services.NewBlueprintProjectPairs(referencingBps), 
errors.Conflict.New("Found one or more references to this connection")
+               return services.NewBlueprintProjectPairs(referencingBps), 
errors.Conflict.New("Found one or more blueprint/project references to this 
connection")
+       }
+       scopeModel := c.getScopeModel()
+       count, err := c.db.Count(dal.From(scopeModel.TableName()), 
dal.Where("connection_id = ?", connectionId))
+       if err != nil {
+               return nil, errors.Default.Wrap(err, fmt.Sprintf("error 
deleting scopes for plugin %s using connection %d", c.pluginName, connectionId))
+       }
+       if count > 0 {
+               return nil, errors.Conflict.New("Found one or more scope 
references to this connection")
        }
        return nil, CallDB(c.db.Delete, connection)
 }
@@ -138,3 +150,12 @@ func (c *ConnectionApiHelper) save(connection interface{}, 
method func(entity in
        }
        return nil
 }
+
+func (c *ConnectionApiHelper) getScopeModel() plugin.ToolLayerScope {
+       pluginMeta, _ := plugin.GetPlugin(c.pluginName)
+       pluginSrc, ok := pluginMeta.(plugin.PluginSource)
+       if !ok {
+               return nil
+       }
+       return pluginSrc.Scope()
+}
diff --git a/backend/helpers/pluginhelper/api/misc_helpers.go 
b/backend/helpers/pluginhelper/api/misc_helpers.go
index f5bfb2445..e7315ceab 100644
--- a/backend/helpers/pluginhelper/api/misc_helpers.go
+++ b/backend/helpers/pluginhelper/api/misc_helpers.go
@@ -25,7 +25,7 @@ import (
 
 // CallDB wraps DB calls with this signature, and handles the case if the 
struct is wrapped in a models.DynamicTabler.
 func CallDB(f func(any, ...dal.Clause) errors.Error, x any, clauses 
...dal.Clause) errors.Error {
-       if dynamic, ok := x.(*models.DynamicTabler); ok {
+       if dynamic, ok := x.(models.DynamicTabler); ok {
                clauses = append(clauses, dal.From(dynamic.TableName()))
                x = dynamic.Unwrap()
        }
diff --git a/backend/helpers/pluginhelper/api/scope_generic_helper.go 
b/backend/helpers/pluginhelper/api/scope_generic_helper.go
index fb0d2dc18..449e76ba7 100644
--- a/backend/helpers/pluginhelper/api/scope_generic_helper.go
+++ b/backend/helpers/pluginhelper/api/scope_generic_helper.go
@@ -23,7 +23,6 @@ import (
        "reflect"
        "strconv"
        "strings"
-       "sync"
        "time"
 
        "github.com/apache/incubator-devlake/core/context"
@@ -38,11 +37,6 @@ import (
        "github.com/mitchellh/mapstructure"
 )
 
-var (
-       tablesCache       []string // these cached vars can probably be moved 
somewhere more centralized later
-       tablesCacheLoader = new(sync.Once)
-)
-
 type NoScopeConfig struct{}
 
 type (
@@ -641,14 +635,6 @@ func createDeleteQuery(tableName string, scopeIdKey 
string, scopeId string) stri
        return query
 }
 
-func (gs *GenericScopeApiHelper[Conn, Scope, ScopeConfig]) lazyCacheTables() 
errors.Error {
-       var err errors.Error
-       tablesCacheLoader.Do(func() {
-               tablesCache, err = gs.db.AllTables()
-       })
-       return err
-}
-
 func (gs *GenericScopeApiHelper[Conn, Scope, ScopeConfig]) 
getAffectedTables(pluginName string) ([]string, errors.Error) {
        var tables []string
        meta, err := plugin.GetPlugin(pluginName)
@@ -658,11 +644,14 @@ func (gs *GenericScopeApiHelper[Conn, Scope, 
ScopeConfig]) getAffectedTables(plu
        if pluginModel, ok := meta.(plugin.PluginModel); !ok {
                return nil, errors.Default.New(fmt.Sprintf("plugin \"%s\" does 
not implement listing its tables", pluginName))
        } else {
-               if err = gs.lazyCacheTables(); err != nil {
+               // Unfortunately, can't cache the tables because Python creates 
some tables on a per-demand basis, so such a cache would possibly get outdated.
+               // It's a rare scenario in practice, but might as well play it 
safe and sacrifice some performance here
+               var allTables []string
+               if allTables, err = gs.db.AllTables(); err != nil {
                        return nil, err
                }
                // collect raw tables
-               for _, table := range tablesCache {
+               for _, table := range allTables {
                        if strings.HasPrefix(table, "_raw_"+pluginName) {
                                tables = append(tables, table)
                        }
@@ -690,8 +679,6 @@ func (gs *GenericScopeApiHelper[Conn, Scope, ScopeConfig]) 
getAffectedTables(plu
 }
 
 func isScopeModel(obj dal.Tabler) bool {
-       if _, ok := obj.(plugin.ToolLayerScope); ok {
-               return true
-       }
-       return reflectField(obj, "ScopeConfigId").IsValid()
+       _, ok := obj.(plugin.ToolLayerScope)
+       return ok
 }
diff --git a/backend/helpers/pluginhelper/api/scope_helper_test.go 
b/backend/helpers/pluginhelper/api/scope_helper_test.go
index b05701693..8a3806b65 100644
--- a/backend/helpers/pluginhelper/api/scope_helper_test.go
+++ b/backend/helpers/pluginhelper/api/scope_helper_test.go
@@ -305,7 +305,7 @@ func createMockScopeHelper[Repo any](scopeIdFieldName 
string) *ScopeApiHelper[Te
        mockDal.On("All", mock.Anything, mock.Anything).Return(nil)
        mockDal.On("AllTables").Return(nil, nil)
 
-       connHelper := NewConnectionHelper(mockRes, nil)
+       connHelper := NewConnectionHelper(mockRes, nil, "dummy_plugin")
 
        params := &ReflectionParameters{
                ScopeIdFieldName:  scopeIdFieldName,
diff --git a/backend/plugins/ae/api/connection.go 
b/backend/plugins/ae/api/connection.go
index 00a9d3c99..a72d6c247 100644
--- a/backend/plugins/ae/api/connection.go
+++ b/backend/plugins/ae/api/connection.go
@@ -145,7 +145,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/ae/api/init.go b/backend/plugins/ae/api/init.go
index d92c2b334..1da6ff414 100644
--- a/backend/plugins/ae/api/init.go
+++ b/backend/plugins/ae/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/go-playground/validator/v10"
 )
@@ -27,11 +28,13 @@ var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
 }
diff --git a/backend/plugins/ae/impl/impl.go b/backend/plugins/ae/impl/impl.go
index 998f00ace..db56e84ed 100644
--- a/backend/plugins/ae/impl/impl.go
+++ b/backend/plugins/ae/impl/impl.go
@@ -31,18 +31,34 @@ import (
        "github.com/apache/incubator-devlake/plugins/ae/tasks"
 )
 
-var _ plugin.PluginMeta = (*AE)(nil)
-var _ plugin.PluginInit = (*AE)(nil)
-var _ plugin.PluginTask = (*AE)(nil)
-var _ plugin.PluginApi = (*AE)(nil)
-var _ plugin.PluginModel = (*AE)(nil)
-var _ plugin.PluginMigration = (*AE)(nil)
-var _ plugin.CloseablePluginTask = (*AE)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+} = (*AE)(nil)
 
 type AE struct{}
 
 func (p AE) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
+       return nil
+}
+
+func (p AE) Connection() dal.Tabler {
+       return &models.AeConnection{}
+}
+
+func (p AE) Scope() plugin.ToolLayerScope {
+       return nil
+}
+
+func (p AE) ScopeConfig() dal.Tabler {
        return nil
 }
 
@@ -58,6 +74,10 @@ func (p AE) Description() string {
        return "To collect and enrich data from AE"
 }
 
+func (p AE) Name() string {
+       return "ae"
+}
+
 func (p AE) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectProjectMeta,
@@ -81,6 +101,7 @@ func (p AE) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]inter
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        err = connectionHelper.FirstById(connection, op.ConnectionId)
        if err != nil {
diff --git a/backend/plugins/bamboo/api/blueprint_V200_test.go 
b/backend/plugins/bamboo/api/blueprint_V200_test.go
index 2c171b5c2..ce74bd2b7 100644
--- a/backend/plugins/bamboo/api/blueprint_V200_test.go
+++ b/backend/plugins/bamboo/api/blueprint_V200_test.go
@@ -124,6 +124,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
 
        // register bamboo plugin for NewDomainIdGenerator
        mockMeta := mockplugin.NewPluginMeta(t)
+       mockMeta.On("Name").Return("bamboo").Maybe()
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/bamboo")
        err = plugin.RegisterPlugin("bamboo", mockMeta)
        assert.Equal(t, err, nil)
@@ -145,7 +146,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
                        *dst = *testScopeConfig
                }).Return(nil)
        })
-       Init(basicRes)
+       Init(basicRes, mockMeta)
 
        plans, scopes, err := MakePipelinePlanV200(testSubTaskMeta, 
testConnectionID, bpScopes, syncPolicy)
        assert.Equal(t, err, nil)
diff --git a/backend/plugins/bamboo/api/connection.go 
b/backend/plugins/bamboo/api/connection.go
index 7c1995ac8..98e20ea67 100644
--- a/backend/plugins/bamboo/api/connection.go
+++ b/backend/plugins/bamboo/api/connection.go
@@ -118,7 +118,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/bamboo/api/init.go 
b/backend/plugins/bamboo/api/init.go
index 251bc19e6..59cf12a83 100644
--- a/backend/plugins/bamboo/api/init.go
+++ b/backend/plugins/bamboo/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/bamboo/models"
        "github.com/go-playground/validator/v10"
@@ -32,12 +33,14 @@ var scopeConfigHelper 
*api.ScopeConfigHelper[models.BambooScopeConfig]
 
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "ProjectKey",
diff --git a/backend/plugins/bamboo/impl/impl.go 
b/backend/plugins/bamboo/impl/impl.go
index 714ad3bbf..f187af714 100644
--- a/backend/plugins/bamboo/impl/impl.go
+++ b/backend/plugins/bamboo/impl/impl.go
@@ -33,33 +33,35 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Bamboo)(nil)
-var _ plugin.PluginInit = (*Bamboo)(nil)
-var _ plugin.PluginTask = (*Bamboo)(nil)
-var _ plugin.PluginModel = (*Bamboo)(nil)
-var _ plugin.PluginMigration = (*Bamboo)(nil)
-var _ plugin.DataSourcePluginBlueprintV200 = (*Bamboo)(nil)
-var _ plugin.CloseablePluginTask = (*Bamboo)(nil)
-
-// var _ plugin.PluginSource = (*Bamboo)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.DataSourcePluginBlueprintV200
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+} = (*Bamboo)(nil)
 
 type Bamboo struct{}
 
 func (p Bamboo) Init(br context.BasicRes) errors.Error {
-       api.Init(br)
+       api.Init(br, p)
+
        return nil
 }
 
-func (p Bamboo) Connection() interface{} {
+func (p Bamboo) Connection() dal.Tabler {
        return &models.BambooConnection{}
 }
 
-func (p Bamboo) Scope() interface{} {
-       return nil
+func (p Bamboo) Scope() plugin.ToolLayerScope {
+       return &models.BambooProject{}
 }
 
-func (p Bamboo) ScopeConfig() interface{} {
-       return nil
+func (p Bamboo) ScopeConfig() dal.Tabler {
+       return &models.BambooScopeConfig{}
 }
 
 func (p Bamboo) MakeDataSourcePipelinePlanV200(connectionId uint64, scopes 
[]*plugin.BlueprintScopeV200, syncPolicy plugin.BlueprintSyncPolicy) 
(plugin.PipelinePlan, []plugin.Scope, errors.Error) {
@@ -82,6 +84,10 @@ func (p Bamboo) Description() string {
        return "collect some Bamboo data"
 }
 
+func (p Bamboo) Name() string {
+       return "bamboo"
+}
+
 func (p Bamboo) SubTaskMetas() []plugin.SubTaskMeta {
        // TODO add your sub task here
        return []plugin.SubTaskMeta{
@@ -117,6 +123,7 @@ func (p Bamboo) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.BambooConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/bitbucket/api/blueprint_V200_test.go 
b/backend/plugins/bitbucket/api/blueprint_V200_test.go
index d2d546e98..4a2efb735 100644
--- a/backend/plugins/bitbucket/api/blueprint_V200_test.go
+++ b/backend/plugins/bitbucket/api/blueprint_V200_test.go
@@ -56,10 +56,12 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        }
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/bitbucket")
+       mockMeta.On("Name").Return("bitbucket").Maybe()
        err := plugin.RegisterPlugin("bitbucket", mockMeta)
        assert.Nil(t, err)
        // Refresh Global Variables and set the sql mock
-       mockBasicRes()
+       mockBasicRes(t)
+
        bs := &plugin.BlueprintScopeV200{
                Id: "1",
        }
@@ -129,7 +131,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        assert.Equal(t, expectScopes, scopes)
 }
 
-func mockBasicRes() {
+func mockBasicRes(t *testing.T) {
        testBitbucketRepo := &models.BitbucketRepo{
                ConnectionId:  1,
                BitbucketId:   "likyh/likyhphp",
@@ -165,5 +167,7 @@ func mockBasicRes() {
                        *dst = *testScopeConfig
                }).Return(nil)
        })
-       Init(mockRes)
+       p := mockplugin.NewPluginMeta(t)
+       p.On("Name").Return("dummy").Maybe()
+       Init(mockRes, p)
 }
diff --git a/backend/plugins/bitbucket/api/connection.go 
b/backend/plugins/bitbucket/api/connection.go
index 0f19f90f5..b41e1b29b 100644
--- a/backend/plugins/bitbucket/api/connection.go
+++ b/backend/plugins/bitbucket/api/connection.go
@@ -125,7 +125,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/bitbucket/api/init.go 
b/backend/plugins/bitbucket/api/init.go
index 9b6ae8f92..205ab59b5 100644
--- a/backend/plugins/bitbucket/api/init.go
+++ b/backend/plugins/bitbucket/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/bitbucket/models"
        "github.com/go-playground/validator/v10"
@@ -31,12 +32,14 @@ var remoteHelper 
*api.RemoteApiHelper[models.BitbucketConnection, models.Bitbuck
 var scHelper *api.ScopeConfigHelper[models.BitbucketScopeConfig]
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "BitbucketId",
diff --git a/backend/plugins/bitbucket/impl/impl.go 
b/backend/plugins/bitbucket/impl/impl.go
index ab640b6f3..e04fb4c1b 100644
--- a/backend/plugins/bitbucket/impl/impl.go
+++ b/backend/plugins/bitbucket/impl/impl.go
@@ -33,33 +33,35 @@ import (
        "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
 )
 
-var _ plugin.PluginMeta = (*Bitbucket)(nil)
-var _ plugin.PluginInit = (*Bitbucket)(nil)
-var _ plugin.PluginTask = (*Bitbucket)(nil)
-var _ plugin.PluginApi = (*Bitbucket)(nil)
-var _ plugin.PluginModel = (*Bitbucket)(nil)
-var _ plugin.PluginMigration = (*Bitbucket)(nil)
-var _ plugin.CloseablePluginTask = (*Bitbucket)(nil)
-var _ plugin.DataSourcePluginBlueprintV200 = (*Bitbucket)(nil)
-
-// var _ plugin.PluginSource = (*Bitbucket)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+       plugin.DataSourcePluginBlueprintV200
+       plugin.PluginSource
+} = (*Bitbucket)(nil)
 
 type Bitbucket string
 
-func (p Bitbucket) Connection() interface{} {
+func (p Bitbucket) Connection() dal.Tabler {
        return &models.BitbucketConnection{}
 }
 
-func (p Bitbucket) Scope() interface{} {
+func (p Bitbucket) Scope() plugin.ToolLayerScope {
        return &models.BitbucketRepo{}
 }
 
-func (p Bitbucket) ScopeConfig() interface{} {
+func (p Bitbucket) ScopeConfig() dal.Tabler {
        return &models.BitbucketScopeConfig{}
 }
 
 func (p Bitbucket) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
@@ -82,6 +84,10 @@ func (p Bitbucket) Description() string {
        return "To collect and enrich data from Bitbucket"
 }
 
+func (p Bitbucket) Name() string {
+       return "bitbucket"
+}
+
 func (p Bitbucket) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectApiPullRequestsMeta,
@@ -136,6 +142,7 @@ func (p Bitbucket) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[strin
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.BitbucketConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/customize/impl/impl.go 
b/backend/plugins/customize/impl/impl.go
index 2f8e77206..2425e5a5b 100644
--- a/backend/plugins/customize/impl/impl.go
+++ b/backend/plugins/customize/impl/impl.go
@@ -28,11 +28,13 @@ import (
        "github.com/mitchellh/mapstructure"
 )
 
-var _ plugin.PluginMeta = (*Customize)(nil)
-var _ plugin.PluginInit = (*Customize)(nil)
-var _ plugin.PluginApi = (*Customize)(nil)
-var _ plugin.PluginModel = (*Customize)(nil)
-var _ plugin.PluginMigration = (*Customize)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+} = (*Customize)(nil)
 
 type Customize struct {
        handlers *api.Handlers
@@ -72,6 +74,10 @@ func (p Customize) Description() string {
        return "To customize table fields"
 }
 
+func (p Customize) Name() string {
+       return "customize"
+}
+
 func (p Customize) MigrationScripts() []plugin.MigrationScript {
        return migrationscripts.All()
 }
diff --git a/backend/plugins/dbt/impl/impl.go b/backend/plugins/dbt/impl/impl.go
index fe17c30ba..88eafa9f6 100644
--- a/backend/plugins/dbt/impl/impl.go
+++ b/backend/plugins/dbt/impl/impl.go
@@ -25,11 +25,11 @@ import (
        "github.com/apache/incubator-devlake/plugins/dbt/tasks"
 )
 
-var (
-       _ plugin.PluginMeta  = (*Dbt)(nil)
-       _ plugin.PluginTask  = (*Dbt)(nil)
-       _ plugin.PluginModel = (*Dbt)(nil)
-)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginTask
+       plugin.PluginModel
+} = (*Dbt)(nil)
 
 type Dbt struct{}
 
@@ -70,3 +70,7 @@ func (p Dbt) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]inte
 func (p Dbt) RootPkgPath() string {
        return "github.com/apache/incubator-devlake/plugins/dbt"
 }
+
+func (p Dbt) Name() string {
+       return "dbt"
+}
diff --git a/backend/plugins/dora/impl/impl.go 
b/backend/plugins/dora/impl/impl.go
index 5a57ea20b..7f76b2bcc 100644
--- a/backend/plugins/dora/impl/impl.go
+++ b/backend/plugins/dora/impl/impl.go
@@ -28,12 +28,14 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Dora)(nil)
-var _ plugin.PluginTask = (*Dora)(nil)
-var _ plugin.PluginModel = (*Dora)(nil)
-var _ plugin.PluginMetric = (*Dora)(nil)
-var _ plugin.PluginMigration = (*Dora)(nil)
-var _ plugin.MetricPluginBlueprintV200 = (*Dora)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginTask
+       plugin.PluginModel
+       plugin.PluginMetric
+       plugin.PluginMigration
+       plugin.MetricPluginBlueprintV200
+} = (*Dora)(nil)
 
 type Dora struct{}
 
@@ -68,6 +70,10 @@ func (p Dora) GetTablesInfo() []dal.Tabler {
        return []dal.Tabler{}
 }
 
+func (p Dora) Name() string {
+       return "dora"
+}
+
 func (p Dora) IsProjectMetric() bool {
        return true
 }
diff --git a/backend/plugins/feishu/api/connection.go 
b/backend/plugins/feishu/api/connection.go
index a570f6208..6c6f3cf0b 100644
--- a/backend/plugins/feishu/api/connection.go
+++ b/backend/plugins/feishu/api/connection.go
@@ -111,7 +111,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/feishu/api/init.go 
b/backend/plugins/feishu/api/init.go
index d92c2b334..7a16d5f0c 100644
--- a/backend/plugins/feishu/api/init.go
+++ b/backend/plugins/feishu/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/go-playground/validator/v10"
 )
@@ -27,11 +28,12 @@ var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
 }
diff --git a/backend/plugins/feishu/impl/impl.go 
b/backend/plugins/feishu/impl/impl.go
index 113463a5d..799466f40 100644
--- a/backend/plugins/feishu/impl/impl.go
+++ b/backend/plugins/feishu/impl/impl.go
@@ -31,18 +31,22 @@ import (
        "github.com/apache/incubator-devlake/plugins/feishu/tasks"
 )
 
-var _ plugin.PluginMeta = (*Feishu)(nil)
-var _ plugin.PluginInit = (*Feishu)(nil)
-var _ plugin.PluginTask = (*Feishu)(nil)
-var _ plugin.PluginApi = (*Feishu)(nil)
-var _ plugin.PluginModel = (*Feishu)(nil)
-var _ plugin.PluginMigration = (*Feishu)(nil)
-var _ plugin.CloseablePluginTask = (*Feishu)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginSource
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+} = (*Feishu)(nil)
 
 type Feishu struct{}
 
 func (p Feishu) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
@@ -57,6 +61,22 @@ func (p Feishu) Description() string {
        return "To collect and enrich data from Feishu"
 }
 
+func (p Feishu) Name() string {
+       return "feishu"
+}
+
+func (p Feishu) Connection() dal.Tabler {
+       return &models.FeishuConnection{}
+}
+
+func (p Feishu) Scope() plugin.ToolLayerScope {
+       return nil
+}
+
+func (p Feishu) ScopeConfig() dal.Tabler {
+       return nil
+}
+
 func (p Feishu) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectChatMeta,
@@ -79,6 +99,7 @@ func (p Feishu) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.FeishuConnection{}
        err := connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/gitee/api/connection.go 
b/backend/plugins/gitee/api/connection.go
index 0bb87fa59..6024366b6 100644
--- a/backend/plugins/gitee/api/connection.go
+++ b/backend/plugins/gitee/api/connection.go
@@ -128,7 +128,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/gitee/api/init.go 
b/backend/plugins/gitee/api/init.go
index d92c2b334..1da6ff414 100644
--- a/backend/plugins/gitee/api/init.go
+++ b/backend/plugins/gitee/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/go-playground/validator/v10"
 )
@@ -27,11 +28,13 @@ var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
 }
diff --git a/backend/plugins/gitee/impl/impl.go 
b/backend/plugins/gitee/impl/impl.go
index 9ef766354..5a1510cf2 100644
--- a/backend/plugins/gitee/impl/impl.go
+++ b/backend/plugins/gitee/impl/impl.go
@@ -31,23 +31,42 @@ import (
        "github.com/apache/incubator-devlake/plugins/gitee/tasks"
 )
 
-var _ plugin.PluginMeta = (*Gitee)(nil)
-var _ plugin.PluginInit = (*Gitee)(nil)
-var _ plugin.PluginTask = (*Gitee)(nil)
-var _ plugin.PluginApi = (*Gitee)(nil)
-var _ plugin.PluginModel = (*Gitee)(nil)
-var _ plugin.PluginMigration = (*Gitee)(nil)
-var _ plugin.CloseablePluginTask = (*Gitee)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginSource
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+} = (*Gitee)(nil)
+
+var _ plugin.PluginSource = (*Gitee)(nil)
 
 type Gitee string
 
+func (p Gitee) Connection() dal.Tabler {
+       return &models.GiteeConnection{}
+}
+
+func (p Gitee) Scope() plugin.ToolLayerScope {
+       return &models.GiteeRepo{}
+}
+
+func (p Gitee) ScopeConfig() dal.Tabler {
+       return nil
+}
+
 func (p Gitee) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
 func (p Gitee) GetTablesInfo() []dal.Tabler {
-       return []dal.Tabler{&models.GiteeConnection{},
+       return []dal.Tabler{
+               &models.GiteeConnection{},
                &models.GiteeAccount{},
                &models.GiteeCommit{},
                &models.GiteeCommitStat{},
@@ -69,6 +88,10 @@ func (p Gitee) Description() string {
        return "To collect and enrich data from Gitee"
 }
 
+func (p Gitee) Name() string {
+       return "gitee"
+}
+
 func (p Gitee) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectApiRepoMeta,
@@ -127,6 +150,7 @@ func (p Gitee) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]in
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
 
        if err != nil {
diff --git a/backend/plugins/gitee/models/repo.go 
b/backend/plugins/gitee/models/repo.go
index 70d577237..1a9648d25 100644
--- a/backend/plugins/gitee/models/repo.go
+++ b/backend/plugins/gitee/models/repo.go
@@ -19,6 +19,8 @@ package models
 
 import (
        "github.com/apache/incubator-devlake/core/models/common"
+       "github.com/apache/incubator-devlake/core/plugin"
+       "strconv"
        "time"
 )
 
@@ -38,6 +40,16 @@ type GiteeRepo struct {
        common.NoPKModel
 }
 
+func (r GiteeRepo) ScopeId() string {
+       return strconv.Itoa(r.GiteeId)
+}
+
+func (r GiteeRepo) ScopeName() string {
+       return r.Name
+}
+
 func (GiteeRepo) TableName() string {
        return "_tool_gitee_repos"
 }
+
+var _ plugin.ToolLayerScope = (*GiteeRepo)(nil)
diff --git a/backend/plugins/gitextractor/impl/impl.go 
b/backend/plugins/gitextractor/impl/impl.go
index 88291334c..4c0c64e50 100644
--- a/backend/plugins/gitextractor/impl/impl.go
+++ b/backend/plugins/gitextractor/impl/impl.go
@@ -31,9 +31,11 @@ import (
        "strings"
 )
 
-var _ plugin.PluginMeta = (*GitExtractor)(nil)
-var _ plugin.PluginTask = (*GitExtractor)(nil)
-var _ plugin.PluginModel = (*GitExtractor)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginTask
+       plugin.PluginModel
+} = (*GitExtractor)(nil)
 
 type GitExtractor struct{}
 
@@ -45,6 +47,10 @@ func (p GitExtractor) Description() string {
        return "extract infos from git repository"
 }
 
+func (p GitExtractor) Name() string {
+       return "gitextractor"
+}
+
 // return all available subtasks, framework will run them for you in order
 func (p GitExtractor) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
diff --git a/backend/plugins/github/api/blueprint_V200_test.go 
b/backend/plugins/github/api/blueprint_V200_test.go
index 2bbfe7a8c..4f1542304 100644
--- a/backend/plugins/github/api/blueprint_V200_test.go
+++ b/backend/plugins/github/api/blueprint_V200_test.go
@@ -57,10 +57,12 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        }
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/github")
+       mockMeta.On("Name").Return("github").Maybe()
        err := plugin.RegisterPlugin("github", mockMeta)
        assert.Nil(t, err)
        // Refresh Global Variables and set the sql mock
-       mockBasicRes()
+       mockBasicRes(t)
+
        bs := &plugin.BlueprintScopeV200{
                Id: "1",
        }
@@ -131,7 +133,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        assert.Equal(t, expectScopes, scopes)
 }
 
-func mockBasicRes() {
+func mockBasicRes(t *testing.T) {
        testGithubRepo := &models.GithubRepo{
                ConnectionId:  1,
                GithubId:      12345,
@@ -168,5 +170,7 @@ func mockBasicRes() {
                        *dst = *testScopeConfig
                }).Return(nil)
        })
-       Init(mockRes)
+       p := mockplugin.NewPluginMeta(t)
+       p.On("Name").Return("dummy").Maybe()
+       Init(mockRes, p)
 }
diff --git a/backend/plugins/github/api/connection.go 
b/backend/plugins/github/api/connection.go
index 72894c500..f4a41466f 100644
--- a/backend/plugins/github/api/connection.go
+++ b/backend/plugins/github/api/connection.go
@@ -253,7 +253,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/github/api/init.go 
b/backend/plugins/github/api/init.go
index 0a45a3568..45580331e 100644
--- a/backend/plugins/github/api/init.go
+++ b/backend/plugins/github/api/init.go
@@ -36,12 +36,14 @@ var basicRes context.BasicRes
 var scHelper *api.ScopeConfigHelper[models.GithubScopeConfig]
 var remoteHelper *api.RemoteApiHelper[models.GithubConnection, 
models.GithubRepo, repo, plugin.ApiGroup]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "GithubId",
diff --git a/backend/plugins/github/impl/impl.go 
b/backend/plugins/github/impl/impl.go
index 3b7dbfe2d..2e354bf99 100644
--- a/backend/plugins/github/impl/impl.go
+++ b/backend/plugins/github/impl/impl.go
@@ -34,32 +34,34 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/tasks"
 )
 
-var _ plugin.PluginMeta = (*Github)(nil)
-var _ plugin.PluginInit = (*Github)(nil)
-var _ plugin.PluginTask = (*Github)(nil)
-var _ plugin.PluginApi = (*Github)(nil)
-var _ plugin.PluginModel = (*Github)(nil)
-var _ plugin.DataSourcePluginBlueprintV200 = (*Github)(nil)
-var _ plugin.CloseablePluginTask = (*Github)(nil)
-
-// var _ plugin.PluginSource = (*Github)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginSource
+       plugin.DataSourcePluginBlueprintV200
+       plugin.CloseablePluginTask
+} = (*Github)(nil)
 
 type Github struct{}
 
-func (p Github) Connection() interface{} {
+func (p Github) Connection() dal.Tabler {
        return &models.GithubConnection{}
 }
 
-func (p Github) Scope() interface{} {
+func (p Github) Scope() plugin.ToolLayerScope {
        return &models.GithubRepo{}
 }
 
-func (p Github) ScopeConfig() interface{} {
+func (p Github) ScopeConfig() dal.Tabler {
        return &models.GithubScopeConfig{}
 }
 
 func (p Github) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
@@ -94,6 +96,10 @@ func (p Github) Description() string {
        return "To collect and enrich data from GitHub"
 }
 
+func (p Github) Name() string {
+       return "github"
+}
+
 func (p Github) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectApiIssuesMeta,
@@ -154,6 +160,7 @@ func (p Github) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.GithubConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/github_graphql/impl/impl.go 
b/backend/plugins/github_graphql/impl/impl.go
index 952f519c7..599e84d6a 100644
--- a/backend/plugins/github_graphql/impl/impl.go
+++ b/backend/plugins/github_graphql/impl/impl.go
@@ -41,25 +41,26 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*GithubGraphql)(nil)
-var _ plugin.PluginTask = (*GithubGraphql)(nil)
-var _ plugin.PluginApi = (*GithubGraphql)(nil)
-var _ plugin.PluginModel = (*GithubGraphql)(nil)
-var _ plugin.CloseablePluginTask = (*GithubGraphql)(nil)
-
-// var _ plugin.PluginSource = (*GithubGraphql)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginSource
+       plugin.CloseablePluginTask
+} = (*GithubGraphql)(nil)
 
 type GithubGraphql struct{}
 
-func (p GithubGraphql) Connection() interface{} {
+func (p GithubGraphql) Connection() dal.Tabler {
        return &models.GithubConnection{}
 }
 
-func (p GithubGraphql) Scope() interface{} {
+func (p GithubGraphql) Scope() plugin.ToolLayerScope {
        return &models.GithubRepo{}
 }
 
-func (p GithubGraphql) ScopeConfig() interface{} {
+func (p GithubGraphql) ScopeConfig() dal.Tabler {
        return &models.GithubScopeConfig{}
 }
 
@@ -67,6 +68,10 @@ func (p GithubGraphql) Description() string {
        return "collect some GithubGraphql data"
 }
 
+func (p GithubGraphql) Name() string {
+       return "github_graphql"
+}
+
 func (p GithubGraphql) GetTablesInfo() []dal.Tabler {
        return []dal.Tabler{}
 }
@@ -139,6 +144,7 @@ func (p GithubGraphql) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[s
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.GithubConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/gitlab/api/blueprint_V200_test.go 
b/backend/plugins/gitlab/api/blueprint_V200_test.go
index 7a026f6a6..9241095a6 100644
--- a/backend/plugins/gitlab/api/blueprint_V200_test.go
+++ b/backend/plugins/gitlab/api/blueprint_V200_test.go
@@ -178,6 +178,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        // register gitlab plugin for NewDomainIdGenerator
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/gitlab")
+       mockMeta.On("Name").Return("dummy").Maybe()
        err = plugin.RegisterPlugin("gitlab", mockMeta)
        assert.Equal(t, err, nil)
 
@@ -198,7 +199,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
                        *dst = *testScopeConfig
                }).Return(nil)
        })
-       Init(mockRes)
+       Init(mockRes, mockMeta)
 
        plans, scopes, err := MakePipelinePlanV200(testSubTaskMeta, 
testConnectionID, bpScopes, syncPolicy)
        assert.Equal(t, err, nil)
diff --git a/backend/plugins/gitlab/api/connection.go 
b/backend/plugins/gitlab/api/connection.go
index df6bece61..dedca47b9 100644
--- a/backend/plugins/gitlab/api/connection.go
+++ b/backend/plugins/gitlab/api/connection.go
@@ -132,7 +132,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/gitlab/api/init.go 
b/backend/plugins/gitlab/api/init.go
index 347d103ad..c929569f2 100644
--- a/backend/plugins/gitlab/api/init.go
+++ b/backend/plugins/gitlab/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "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/go-playground/validator/v10"
@@ -31,12 +32,14 @@ var remoteHelper 
*api.RemoteApiHelper[models.GitlabConnection, models.GitlabProj
 var basicRes context.BasicRes
 var scHelper *api.ScopeConfigHelper[models.GitlabScopeConfig]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "GitlabId",
diff --git a/backend/plugins/gitlab/impl/impl.go 
b/backend/plugins/gitlab/impl/impl.go
index fd7bb02e6..72060fbeb 100644
--- a/backend/plugins/gitlab/impl/impl.go
+++ b/backend/plugins/gitlab/impl/impl.go
@@ -40,9 +40,9 @@ var _ interface {
        plugin.PluginTask
        plugin.PluginModel
        plugin.PluginMigration
+       plugin.PluginSource
        plugin.DataSourcePluginBlueprintV200
        plugin.CloseablePluginTask
-       // plugin.PluginSource
 } = (*Gitlab)(nil)
 
 type Gitlab string
@@ -55,19 +55,20 @@ func init() {
 }
 
 func (p Gitlab) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
-func (p Gitlab) Connection() interface{} {
+func (p Gitlab) Connection() dal.Tabler {
        return &models.GitlabConnection{}
 }
 
-func (p Gitlab) Scope() interface{} {
+func (p Gitlab) Scope() plugin.ToolLayerScope {
        return &models.GitlabProject{}
 }
 
-func (p Gitlab) ScopeConfig() interface{} {
+func (p Gitlab) ScopeConfig() dal.Tabler {
        return &models.GitlabScopeConfig{}
 }
 
@@ -101,6 +102,10 @@ func (p Gitlab) Description() string {
        return "To collect and enrich data from Gitlab"
 }
 
+func (p Gitlab) Name() string {
+       return "gitlab"
+}
+
 func (p Gitlab) SubTaskMetas() []plugin.SubTaskMeta {
        list, err := 
subtaskmeta_sorter.NewDependencySorter(tasks.SubTaskMetaList).Sort()
        if err != nil {
@@ -123,6 +128,7 @@ func (p Gitlab) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        if err != nil {
                return nil, err
diff --git a/backend/plugins/icla/impl/impl.go 
b/backend/plugins/icla/impl/impl.go
index a959ee54c..3043d8cb5 100644
--- a/backend/plugins/icla/impl/impl.go
+++ b/backend/plugins/icla/impl/impl.go
@@ -30,13 +30,16 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Icla)(nil)
-var _ plugin.PluginInit = (*Icla)(nil)
-var _ plugin.PluginTask = (*Icla)(nil)
-var _ plugin.PluginApi = (*Icla)(nil)
-var _ plugin.PluginModel = (*Icla)(nil)
-var _ plugin.PluginMigration = (*Icla)(nil)
-var _ plugin.CloseablePluginTask = (*Icla)(nil)
+
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+} = (*Icla)(nil)
 
 type Icla struct{}
 
@@ -84,6 +87,10 @@ func (p Icla) RootPkgPath() string {
        return "github.com/apache/incubator-devlake/plugins/icla"
 }
 
+func (p Icla) Name() string {
+       return "icla"
+}
+
 func (p Icla) MigrationScripts() []plugin.MigrationScript {
        return migrationscripts.All()
 }
diff --git a/backend/plugins/jenkins/api/blueprint_v200_test.go 
b/backend/plugins/jenkins/api/blueprint_v200_test.go
index d409ac544..db4c4fa08 100644
--- a/backend/plugins/jenkins/api/blueprint_v200_test.go
+++ b/backend/plugins/jenkins/api/blueprint_v200_test.go
@@ -35,6 +35,7 @@ import (
 func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/jenkins")
+       mockMeta.On("Name").Return("jenkins").Maybe()
        err := plugin.RegisterPlugin("jenkins", mockMeta)
        assert.Nil(t, err)
        bs := &plugin.BlueprintScopeV200{
@@ -45,7 +46,8 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        bpScopes := make([]*plugin.BlueprintScopeV200, 0)
        bpScopes = append(bpScopes, bs)
 
-       mockBasicRes()
+       mockBasicRes(t)
+
        plan := make(plugin.PipelinePlan, len(bpScopes))
        plan, err = makeDataSourcePipelinePlanV200(nil, plan, bpScopes, 1, 
syncPolicy)
        assert.Nil(t, err)
@@ -78,7 +80,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        assert.Equal(t, expectScopes, scopes)
 }
 
-func mockBasicRes() {
+func mockBasicRes(t *testing.T) {
        jenkinsJob := &models.JenkinsJob{
                ConnectionId: 1,
                FullName:     "a/b/ccc",
@@ -101,5 +103,7 @@ func mockBasicRes() {
                        *dst = *jenkinsJob
                }).Return(nil)
        })
-       Init(mockRes)
+       p := mockplugin.NewPluginMeta(t)
+       p.On("Name").Return("dummy").Maybe()
+       Init(mockRes, p)
 }
diff --git a/backend/plugins/jenkins/api/connection.go 
b/backend/plugins/jenkins/api/connection.go
index 634063497..c34ea0ed2 100644
--- a/backend/plugins/jenkins/api/connection.go
+++ b/backend/plugins/jenkins/api/connection.go
@@ -134,7 +134,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/jenkins/api/init.go 
b/backend/plugins/jenkins/api/init.go
index aadcae11b..457a22049 100644
--- a/backend/plugins/jenkins/api/init.go
+++ b/backend/plugins/jenkins/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/jenkins/models"
        "github.com/go-playground/validator/v10"
@@ -32,12 +33,14 @@ var remoteHelper 
*api.RemoteApiHelper[models.JenkinsConnection, models.JenkinsJo
 var basicRes context.BasicRes
 var scHelper *api.ScopeConfigHelper[models.JenkinsScopeConfig]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "FullName",
diff --git a/backend/plugins/jenkins/impl/impl.go 
b/backend/plugins/jenkins/impl/impl.go
index 67c5eeebf..f2ead7114 100644
--- a/backend/plugins/jenkins/impl/impl.go
+++ b/backend/plugins/jenkins/impl/impl.go
@@ -35,33 +35,35 @@ import (
        "github.com/apache/incubator-devlake/plugins/jenkins/tasks"
 )
 
-var _ plugin.PluginMeta = (*Jenkins)(nil)
-var _ plugin.PluginInit = (*Jenkins)(nil)
-var _ plugin.PluginTask = (*Jenkins)(nil)
-var _ plugin.PluginApi = (*Jenkins)(nil)
-var _ plugin.PluginModel = (*Jenkins)(nil)
-var _ plugin.PluginMigration = (*Jenkins)(nil)
-var _ plugin.CloseablePluginTask = (*Jenkins)(nil)
-
-// var _ plugin.PluginSource = (*Jenkins)(nil)
-var _ plugin.DataSourcePluginBlueprintV200 = (*Jenkins)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+       plugin.DataSourcePluginBlueprintV200
+} = (*Jenkins)(nil)
 
 type Jenkins struct{}
 
 func (p Jenkins) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
-func (p Jenkins) Connection() interface{} {
+func (p Jenkins) Connection() dal.Tabler {
        return &models.JenkinsConnection{}
 }
 
-func (p Jenkins) Scope() interface{} {
+func (p Jenkins) Scope() plugin.ToolLayerScope {
        return &models.JenkinsJob{}
 }
 
-func (p Jenkins) ScopeConfig() interface{} {
+func (p Jenkins) ScopeConfig() dal.Tabler {
        return &models.JenkinsScopeConfig{}
 }
 
@@ -82,6 +84,10 @@ func (p Jenkins) Description() string {
        return "To collect and enrich data from Jenkins"
 }
 
+func (p Jenkins) Name() string {
+       return "jenkins"
+}
+
 func (p Jenkins) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.ConvertJobsMeta,
@@ -108,6 +114,7 @@ func (p Jenkins) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[string]
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        if err != nil {
                return nil, err
diff --git a/backend/plugins/jira/api/blueprint_v200_test.go 
b/backend/plugins/jira/api/blueprint_v200_test.go
index 6f15c2568..0d4e8aa7d 100644
--- a/backend/plugins/jira/api/blueprint_v200_test.go
+++ b/backend/plugins/jira/api/blueprint_v200_test.go
@@ -35,6 +35,7 @@ import (
 func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/jira")
+       mockMeta.On("Name").Return("jira").Maybe()
        err := plugin.RegisterPlugin("jira", mockMeta)
        assert.Nil(t, err)
        bs := &plugin.BlueprintScopeV200{
@@ -44,7 +45,8 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        bpScopes := make([]*plugin.BlueprintScopeV200, 0)
        bpScopes = append(bpScopes, bs)
        plan := make(plugin.PipelinePlan, len(bpScopes))
-       mockBasicRes()
+       mockBasicRes(t)
+
        plan, err = makeDataSourcePipelinePlanV200(nil, plan, bpScopes, 
uint64(1), syncPolicy)
        assert.Nil(t, err)
        scopes, err := makeScopesV200(bpScopes, uint64(1))
@@ -76,7 +78,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        assert.Equal(t, expectScopes, scopes)
 }
 
-func mockBasicRes() {
+func mockBasicRes(t *testing.T) {
        jiraBoard := &models.JiraBoard{
                ConnectionId: 1,
                BoardId:      10,
@@ -99,5 +101,7 @@ func mockBasicRes() {
                        *dst = *jiraBoard
                }).Return(nil)
        })
-       Init(mockRes)
+       p := mockplugin.NewPluginMeta(t)
+       p.On("Name").Return("dummy").Maybe()
+       Init(mockRes, p)
 }
diff --git a/backend/plugins/jira/api/connection.go 
b/backend/plugins/jira/api/connection.go
index 419af61c5..0b5a92847 100644
--- a/backend/plugins/jira/api/connection.go
+++ b/backend/plugins/jira/api/connection.go
@@ -179,7 +179,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/jira/api/init.go b/backend/plugins/jira/api/init.go
index 9564d0678..b82191c28 100644
--- a/backend/plugins/jira/api/init.go
+++ b/backend/plugins/jira/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/jira/models"
        "github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
@@ -32,12 +33,14 @@ var remoteHelper 
*api.RemoteApiHelper[models.JiraConnection, models.JiraBoard, a
 var basicRes context.BasicRes
 var scHelper *api.ScopeConfigHelper[models.JiraScopeConfig]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "BoardId",
diff --git a/backend/plugins/jira/impl/impl.go 
b/backend/plugins/jira/impl/impl.go
index 4ced7222b..906bcc79c 100644
--- a/backend/plugins/jira/impl/impl.go
+++ b/backend/plugins/jira/impl/impl.go
@@ -42,26 +42,27 @@ var _ interface {
        plugin.PluginMigration
        plugin.DataSourcePluginBlueprintV200
        plugin.CloseablePluginTask
-       // plugin.PluginSource
+       plugin.PluginSource
 } = (*Jira)(nil)
 
 type Jira struct {
 }
 
-func (p Jira) Connection() interface{} {
+func (p Jira) Connection() dal.Tabler {
        return &models.JiraConnection{}
 }
 
-func (p Jira) Scope() interface{} {
+func (p Jira) Scope() plugin.ToolLayerScope {
        return &models.JiraBoard{}
 }
 
-func (p Jira) ScopeConfig() interface{} {
+func (p Jira) ScopeConfig() dal.Tabler {
        return &models.JiraScopeConfig{}
 }
 
 func (p *Jira) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
@@ -93,6 +94,10 @@ func (p Jira) Description() string {
        return "To collect and enrich data from JIRA"
 }
 
+func (p Jira) Name() string {
+       return "jira"
+}
+
 func (p Jira) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectStatusMeta,
@@ -167,6 +172,7 @@ func (p Jira) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]int
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        err = connectionHelper.FirstById(connection, op.ConnectionId)
        if err != nil {
diff --git a/backend/plugins/org/impl/impl.go b/backend/plugins/org/impl/impl.go
index 97f1b23b1..4ee6f9637 100644
--- a/backend/plugins/org/impl/impl.go
+++ b/backend/plugins/org/impl/impl.go
@@ -27,11 +27,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/org/tasks"
 )
 
-var _ plugin.PluginMeta = (*Org)(nil)
-var _ plugin.PluginInit = (*Org)(nil)
-var _ plugin.PluginTask = (*Org)(nil)
-var _ plugin.PluginModel = (*Org)(nil)
-var _ plugin.ProjectMapper = (*Org)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginModel
+       plugin.ProjectMapper
+} = (*Org)(nil)
 
 type Org struct {
        handlers *api.Handlers
@@ -50,6 +52,10 @@ func (p Org) Description() string {
        return "collect data related to team and organization"
 }
 
+func (p Org) Name() string {
+       return "org"
+}
+
 func (p Org) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.ConnectUserAccountsExactMeta,
diff --git a/backend/plugins/pagerduty/api/connection.go 
b/backend/plugins/pagerduty/api/connection.go
index 040a0ea1f..b320b54d8 100644
--- a/backend/plugins/pagerduty/api/connection.go
+++ b/backend/plugins/pagerduty/api/connection.go
@@ -110,7 +110,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/pagerduty/api/init.go 
b/backend/plugins/pagerduty/api/init.go
index b2a035986..eeacbc794 100644
--- a/backend/plugins/pagerduty/api/init.go
+++ b/backend/plugins/pagerduty/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/pagerduty/models"
        "github.com/go-playground/validator/v10"
@@ -31,12 +32,14 @@ var scopeHelper 
*api.ScopeApiHelper[models.PagerDutyConnection, models.Service,
 
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "Id",
diff --git a/backend/plugins/pagerduty/e2e/incident_test.go 
b/backend/plugins/pagerduty/e2e/incident_test.go
index 14ec2038d..210954f26 100644
--- a/backend/plugins/pagerduty/e2e/incident_test.go
+++ b/backend/plugins/pagerduty/e2e/incident_test.go
@@ -33,24 +33,19 @@ import (
 func TestIncidentDataFlow(t *testing.T) {
        var plugin impl.PagerDuty
        dataflowTester := e2ehelper.NewDataFlowTester(t, "pagerduty", plugin)
-       scopeConfig := models.PagerdutyScopeConfig{
-               Name: "rule1",
-       }
        options := tasks.PagerDutyOptions{
                ConnectionId:         1,
                ServiceId:            "PIKL83L",
                ServiceName:          "DevService",
                Tasks:                nil,
-               PagerdutyScopeConfig: &scopeConfig,
+               PagerdutyScopeConfig: nil,
        }
        taskData := &tasks.PagerDutyTaskData{
                Options: &options,
        }
 
-       dataflowTester.FlushTabler(&models.PagerdutyScopeConfig{})
        dataflowTester.FlushTabler(&models.Service{})
        // tx-rule
-       require.NoError(t, dataflowTester.Dal.CreateOrUpdate(&scopeConfig))
        service := models.Service{
                ConnectionId: options.ConnectionId,
                Url:          
fmt.Sprintf("https://keon-test.pagerduty.com/service-directory/%s";, 
options.ServiceId),
diff --git a/backend/plugins/pagerduty/impl/impl.go 
b/backend/plugins/pagerduty/impl/impl.go
index ee274e0ee..e8251e5ac 100644
--- a/backend/plugins/pagerduty/impl/impl.go
+++ b/backend/plugins/pagerduty/impl/impl.go
@@ -33,14 +33,17 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*PagerDuty)(nil)
-var _ plugin.PluginInit = (*PagerDuty)(nil)
-var _ plugin.PluginTask = (*PagerDuty)(nil)
-var _ plugin.PluginApi = (*PagerDuty)(nil)
 
-var _ plugin.PluginModel = (*PagerDuty)(nil)
-var _ plugin.DataSourcePluginBlueprintV200 = (*PagerDuty)(nil)
-var _ plugin.CloseablePluginTask = (*PagerDuty)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.DataSourcePluginBlueprintV200
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+} = (*PagerDuty)(nil)
 
 type PagerDuty struct{}
 
@@ -48,8 +51,25 @@ func (p PagerDuty) Description() string {
        return "collect some PagerDuty data"
 }
 
+func (p PagerDuty) Name() string {
+       return "pagerduty"
+}
+
 func (p PagerDuty) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
+       return nil
+}
+
+func (p PagerDuty) Connection() dal.Tabler {
+       return &models.PagerDutyConnection{}
+}
+
+func (p PagerDuty) Scope() plugin.ToolLayerScope {
+       return &models.Service{}
+}
+
+func (p PagerDuty) ScopeConfig() dal.Tabler {
        return nil
 }
 
@@ -64,10 +84,10 @@ func (p PagerDuty) SubTaskMetas() []plugin.SubTaskMeta {
 
 func (p PagerDuty) GetTablesInfo() []dal.Tabler {
        return []dal.Tabler{
-               models.Service{},
-               models.Incident{},
-               models.User{},
-               models.Assignment{},
+               &models.Service{},
+               &models.Incident{},
+               &models.User{},
+               &models.Assignment{},
        }
 }
 
@@ -79,6 +99,7 @@ func (p PagerDuty) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[strin
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.PagerDutyConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/pagerduty/models/scope_config.go 
b/backend/plugins/pagerduty/models/scope_config.go
index 93c403cfb..cda12a4c5 100644
--- a/backend/plugins/pagerduty/models/scope_config.go
+++ b/backend/plugins/pagerduty/models/scope_config.go
@@ -26,7 +26,3 @@ type PagerdutyScopeConfig struct {
        Name               string `mapstructure:"name" json:"name" 
gorm:"type:varchar(255);index:idx_name_github,unique" validate:"required"`
        ConnectionId       uint64
 }
-
-func (PagerdutyScopeConfig) TableName() string {
-       return "_tool_pagerduty_scope_configs"
-}
diff --git a/backend/plugins/pagerduty/models/service.go 
b/backend/plugins/pagerduty/models/service.go
index 418d3f80c..c66082991 100644
--- a/backend/plugins/pagerduty/models/service.go
+++ b/backend/plugins/pagerduty/models/service.go
@@ -19,6 +19,7 @@ package models
 
 import (
        "github.com/apache/incubator-devlake/core/models/common"
+       "github.com/apache/incubator-devlake/core/plugin"
 )
 
 type Service struct {
@@ -29,6 +30,16 @@ type Service struct {
        Name         string `json:"name" mapstructure:"name"`
 }
 
-func (Service) TableName() string {
+func (s Service) ScopeId() string {
+       return s.Name
+}
+
+func (s Service) ScopeName() string {
+       return s.Name
+}
+
+func (s Service) TableName() string {
        return "_tool_pagerduty_services"
 }
+
+var _ plugin.ToolLayerScope = (*Service)(nil)
diff --git a/backend/plugins/refdiff/impl/impl.go 
b/backend/plugins/refdiff/impl/impl.go
index 518c0a117..56ff8df86 100644
--- a/backend/plugins/refdiff/impl/impl.go
+++ b/backend/plugins/refdiff/impl/impl.go
@@ -26,11 +26,13 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*RefDiff)(nil)
-var _ plugin.PluginTask = (*RefDiff)(nil)
-var _ plugin.PluginApi = (*RefDiff)(nil)
-var _ plugin.PluginModel = (*RefDiff)(nil)
-var _ plugin.PluginMetric = (*RefDiff)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMetric
+} = (*RefDiff)(nil)
 
 type RefDiff struct{}
 
@@ -38,6 +40,10 @@ func (p RefDiff) Description() string {
        return "Calculate commits diff for specified ref pairs based on 
`commits` and `commit_parents` tables"
 }
 
+func (p RefDiff) Name() string {
+       return "refdiff"
+}
+
 func (p RefDiff) RequiredDataEntities() (data []map[string]interface{}, err 
errors.Error) {
        return []map[string]interface{}{}, nil
 }
diff --git a/backend/plugins/slack/api/connection.go 
b/backend/plugins/slack/api/connection.go
index 303c74fb4..fffc8b84e 100644
--- a/backend/plugins/slack/api/connection.go
+++ b/backend/plugins/slack/api/connection.go
@@ -111,7 +111,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/slack/api/init.go 
b/backend/plugins/slack/api/init.go
index d92c2b334..1da6ff414 100644
--- a/backend/plugins/slack/api/init.go
+++ b/backend/plugins/slack/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/go-playground/validator/v10"
 )
@@ -27,11 +28,13 @@ var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
 }
diff --git a/backend/plugins/slack/impl/impl.go 
b/backend/plugins/slack/impl/impl.go
index 896f7955f..98c394e88 100644
--- a/backend/plugins/slack/impl/impl.go
+++ b/backend/plugins/slack/impl/impl.go
@@ -31,18 +31,22 @@ import (
        "github.com/apache/incubator-devlake/plugins/slack/tasks"
 )
 
-var _ plugin.PluginMeta = (*Slack)(nil)
-var _ plugin.PluginInit = (*Slack)(nil)
-var _ plugin.PluginTask = (*Slack)(nil)
-var _ plugin.PluginApi = (*Slack)(nil)
-var _ plugin.PluginModel = (*Slack)(nil)
-var _ plugin.PluginMigration = (*Slack)(nil)
-var _ plugin.CloseablePluginTask = (*Slack)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+} = (*Slack)(nil)
 
 type Slack struct{}
 
 func (p Slack) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
@@ -56,6 +60,22 @@ func (p Slack) Description() string {
        return "To collect and enrich data from Slack"
 }
 
+func (p Slack) Name() string {
+       return "slack"
+}
+
+func (p Slack) Connection() dal.Tabler {
+       return &models.SlackConnection{}
+}
+
+func (p Slack) Scope() plugin.ToolLayerScope {
+       return nil
+}
+
+func (p Slack) ScopeConfig() dal.Tabler {
+       return nil
+}
+
 func (p Slack) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectChannelMeta,
@@ -78,6 +98,7 @@ func (p Slack) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]in
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.SlackConnection{}
        err := connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/sonarqube/api/blueprint_v200_test.go 
b/backend/plugins/sonarqube/api/blueprint_v200_test.go
index aaaf7d857..009fe9a9b 100644
--- a/backend/plugins/sonarqube/api/blueprint_v200_test.go
+++ b/backend/plugins/sonarqube/api/blueprint_v200_test.go
@@ -34,6 +34,7 @@ import (
 func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/sonarqube")
+       mockMeta.On("Name").Return("sonarqube").Maybe()
        err := plugin.RegisterPlugin("sonarqube", mockMeta)
        assert.Nil(t, err)
        bs := &plugin.BlueprintScopeV200{
@@ -46,6 +47,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        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)
 
diff --git a/backend/plugins/sonarqube/api/connection.go 
b/backend/plugins/sonarqube/api/connection.go
index 3a1bb781e..cb8f1a83e 100644
--- a/backend/plugins/sonarqube/api/connection.go
+++ b/backend/plugins/sonarqube/api/connection.go
@@ -139,7 +139,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/sonarqube/api/init.go 
b/backend/plugins/sonarqube/api/init.go
index 52c2bd0a2..52979f941 100644
--- a/backend/plugins/sonarqube/api/init.go
+++ b/backend/plugins/sonarqube/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/sonarqube/models"
        "github.com/go-playground/validator/v10"
@@ -30,12 +31,14 @@ var scopeHelper 
*api.ScopeApiHelper[models.SonarqubeConnection, models.Sonarqube
 var remoteHelper *api.RemoteApiHelper[models.SonarqubeConnection, 
models.SonarqubeProject, models.SonarqubeApiProject, api.NoRemoteGroupResponse]
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "ProjectKey",
diff --git a/backend/plugins/sonarqube/impl/impl.go 
b/backend/plugins/sonarqube/impl/impl.go
index 9a447736b..5d9ccc073 100644
--- a/backend/plugins/sonarqube/impl/impl.go
+++ b/backend/plugins/sonarqube/impl/impl.go
@@ -41,7 +41,7 @@ var _ interface {
        plugin.PluginMigration
        plugin.DataSourcePluginBlueprintV200
        plugin.CloseablePluginTask
-       // plugin.PluginSource
+       plugin.PluginSource
 } = (*Sonarqube)(nil)
 
 type Sonarqube struct{}
@@ -50,20 +50,25 @@ func (p Sonarqube) Description() string {
        return "collect some Sonarqube data"
 }
 
+func (p Sonarqube) Name() string {
+       return "dbt"
+}
+
 func (p Sonarqube) Init(br context.BasicRes) errors.Error {
-       api.Init(br)
+       api.Init(br, p)
+
        return nil
 }
 
-func (p Sonarqube) Connection() interface{} {
+func (p Sonarqube) Connection() dal.Tabler {
        return &models.SonarqubeConnection{}
 }
 
-func (p Sonarqube) Scope() interface{} {
+func (p Sonarqube) Scope() plugin.ToolLayerScope {
        return &models.SonarqubeProject{}
 }
 
-func (p Sonarqube) TransformationRule() interface{} {
+func (p Sonarqube) ScopeConfig() dal.Tabler {
        return nil
 }
 
@@ -109,6 +114,7 @@ func (p Sonarqube) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[strin
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.SonarqubeConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/starrocks/impl/impl.go 
b/backend/plugins/starrocks/impl/impl.go
index 129c989ed..a608d2b46 100644
--- a/backend/plugins/starrocks/impl/impl.go
+++ b/backend/plugins/starrocks/impl/impl.go
@@ -28,9 +28,11 @@ import (
 type StarRocks string
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*StarRocks)(nil)
-var _ plugin.PluginTask = (*StarRocks)(nil)
-var _ plugin.PluginModel = (*StarRocks)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginTask
+       plugin.PluginModel
+} = (*StarRocks)(nil)
 
 func (s StarRocks) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
@@ -58,6 +60,10 @@ func (s StarRocks) Description() string {
        return "Sync data from database to StarRocks"
 }
 
+func (s StarRocks) Name() string {
+       return "starrocks"
+}
+
 func (s StarRocks) RootPkgPath() string {
        return "github.com/merico-dev/lake/plugins/starrocks"
 }
diff --git a/backend/plugins/tapd/api/blueprint_v200_test.go 
b/backend/plugins/tapd/api/blueprint_v200_test.go
index 8d6273d28..6466f06c9 100644
--- a/backend/plugins/tapd/api/blueprint_v200_test.go
+++ b/backend/plugins/tapd/api/blueprint_v200_test.go
@@ -35,6 +35,7 @@ import (
 func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/tapd")
+       mockMeta.On("Name").Return("dummy").Maybe()
        err := plugin.RegisterPlugin("tapd", mockMeta)
        assert.Nil(t, err)
        bs := &plugin.BlueprintScopeV200{
@@ -44,7 +45,8 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        bpScopes := make([]*plugin.BlueprintScopeV200, 0)
        bpScopes = append(bpScopes, bs)
        plan := make(plugin.PipelinePlan, len(bpScopes))
-       mockBasicRes()
+       mockBasicRes(t)
+
        plan, err = makeDataSourcePipelinePlanV200(nil, plan, bpScopes, 
uint64(1), syncPolicy)
        assert.Nil(t, err)
        scopes, err := makeScopesV200(bpScopes, uint64(1))
@@ -77,7 +79,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        assert.Equal(t, expectScopes, scopes)
 }
 
-func mockBasicRes() {
+func mockBasicRes(t *testing.T) {
        tapdWorkspace := &models.TapdWorkspace{
                ConnectionId: 1,
                Id:           10,
@@ -99,5 +101,7 @@ func mockBasicRes() {
                        *dst = *tapdWorkspace
                }).Return(nil)
        })
-       Init(mockRes)
+       p := mockplugin.NewPluginMeta(t)
+       p.On("Name").Return("dummy").Maybe()
+       Init(mockRes, p)
 }
diff --git a/backend/plugins/tapd/api/connection.go 
b/backend/plugins/tapd/api/connection.go
index 7545709b3..2d2add2cf 100644
--- a/backend/plugins/tapd/api/connection.go
+++ b/backend/plugins/tapd/api/connection.go
@@ -130,7 +130,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/tapd/api/init.go b/backend/plugins/tapd/api/init.go
index 2726f40e0..8765f1c17 100644
--- a/backend/plugins/tapd/api/init.go
+++ b/backend/plugins/tapd/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/tapd/models"
        "github.com/go-playground/validator/v10"
@@ -31,12 +32,14 @@ var scopeHelper *api.ScopeApiHelper[models.TapdConnection, 
models.TapdWorkspace,
 var remoteHelper *api.RemoteApiHelper[models.TapdConnection, 
models.TapdWorkspace, models.TapdWorkspace, api.BaseRemoteGroupResponse]
 var scHelper *api.ScopeConfigHelper[models.TapdScopeConfig]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "Id",
diff --git a/backend/plugins/tapd/impl/impl.go 
b/backend/plugins/tapd/impl/impl.go
index 6339e1723..eb0bb9c15 100644
--- a/backend/plugins/tapd/impl/impl.go
+++ b/backend/plugins/tapd/impl/impl.go
@@ -32,21 +32,37 @@ import (
        "github.com/apache/incubator-devlake/plugins/tapd/tasks"
 )
 
-var _ plugin.PluginMeta = (*Tapd)(nil)
-var _ plugin.PluginInit = (*Tapd)(nil)
-var _ plugin.PluginTask = (*Tapd)(nil)
-var _ plugin.PluginApi = (*Tapd)(nil)
-var _ plugin.PluginModel = (*Tapd)(nil)
-var _ plugin.PluginMigration = (*Tapd)(nil)
-var _ plugin.CloseablePluginTask = (*Tapd)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+} = (*Tapd)(nil)
 
 type Tapd struct{}
 
 func (p Tapd) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
+func (p Tapd) Connection() dal.Tabler {
+       return &models.TapdConnection{}
+}
+
+func (p Tapd) Scope() plugin.ToolLayerScope {
+       return &models.TapdWorkspace{}
+}
+
+func (p Tapd) ScopeConfig() dal.Tabler {
+       return &models.TapdScopeConfig{}
+}
+
 func (p Tapd) GetTablesInfo() []dal.Tabler {
        return []dal.Tabler{
                &models.TapdAccount{},
@@ -93,6 +109,10 @@ func (p Tapd) Description() string {
        return "To collect and enrich data from Tapd"
 }
 
+func (p Tapd) Name() string {
+       return "tapd"
+}
+
 func (p Tapd) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.ConvertWorkspaceMeta,
@@ -172,6 +192,7 @@ func (p Tapd) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]int
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        err = connectionHelper.FirstById(connection, op.ConnectionId)
        if err != nil {
diff --git a/backend/plugins/tapd/tasks/shared_test.go 
b/backend/plugins/tapd/tasks/shared_test.go
index bdd052940..764f4f421 100644
--- a/backend/plugins/tapd/tasks/shared_test.go
+++ b/backend/plugins/tapd/tasks/shared_test.go
@@ -240,6 +240,7 @@ func TestGenerateDomainAccountIdForUsers(t *testing.T) {
        }
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/tapd")
+       mockMeta.On("Name").Return("tapd").Maybe()
        err := plugin.RegisterPlugin("tapd", mockMeta)
        assert.Nil(t, err)
        for _, testCase := range testCases {
diff --git a/backend/plugins/teambition/api/connection.go 
b/backend/plugins/teambition/api/connection.go
index 009124591..fa8220f4e 100644
--- a/backend/plugins/teambition/api/connection.go
+++ b/backend/plugins/teambition/api/connection.go
@@ -139,7 +139,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/teambition/api/init.go 
b/backend/plugins/teambition/api/init.go
index cd019a36f..aef9f75f7 100644
--- a/backend/plugins/teambition/api/init.go
+++ b/backend/plugins/teambition/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/go-playground/validator/v10"
 )
@@ -27,11 +28,13 @@ var vld *validator.Validate
 var connectionHelper *helper.ConnectionApiHelper
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = helper.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
 }
diff --git a/backend/plugins/teambition/impl/impl.go 
b/backend/plugins/teambition/impl/impl.go
index d463bac39..3087bedea 100644
--- a/backend/plugins/teambition/impl/impl.go
+++ b/backend/plugins/teambition/impl/impl.go
@@ -33,11 +33,15 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Teambition)(nil)
-var _ plugin.PluginInit = (*Teambition)(nil)
-var _ plugin.PluginTask = (*Teambition)(nil)
-var _ plugin.PluginApi = (*Teambition)(nil)
-var _ plugin.CloseablePluginTask = (*Teambition)(nil)
+
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       plugin.CloseablePluginTask
+       plugin.PluginSource
+} = (*Teambition)(nil)
 
 type Teambition struct{}
 
@@ -45,8 +49,13 @@ func (p Teambition) Description() string {
        return "collect some Teambition data"
 }
 
+func (p Teambition) Name() string {
+       return "teambition"
+}
+
 func (p Teambition) Init(br context.BasicRes) errors.Error {
-       api.Init(br)
+       api.Init(br, p)
+
        return nil
 }
 
@@ -64,6 +73,18 @@ func (p Teambition) GetTablesInfo() []dal.Tabler {
        }
 }
 
+func (p Teambition) Connection() dal.Tabler {
+       return &models.TeambitionConnection{}
+}
+
+func (p Teambition) Scope() plugin.ToolLayerScope {
+       return nil
+}
+
+func (p Teambition) ScopeConfig() dal.Tabler {
+       return nil
+}
+
 func (p Teambition) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectAccountsMeta,
@@ -107,6 +128,7 @@ func (p Teambition) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[stri
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.TeambitionConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/plugins/trello/api/connection.go 
b/backend/plugins/trello/api/connection.go
index d10a900b5..dcb3dc950 100644
--- a/backend/plugins/trello/api/connection.go
+++ b/backend/plugins/trello/api/connection.go
@@ -125,7 +125,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/trello/api/init.go 
b/backend/plugins/trello/api/init.go
index 6b8096d34..a27808fea 100644
--- a/backend/plugins/trello/api/init.go
+++ b/backend/plugins/trello/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/trello/models"
        "github.com/go-playground/validator/v10"
@@ -30,12 +31,13 @@ var scopeHelper 
*api.ScopeApiHelper[models.TrelloConnection, models.TrelloBoard,
 var basicRes context.BasicRes
 var scHelper *api.ScopeConfigHelper[models.TrelloScopeConfig]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "BoardId",
diff --git a/backend/plugins/trello/impl/impl.go 
b/backend/plugins/trello/impl/impl.go
index 0a3fb9b4f..199d6ae49 100644
--- a/backend/plugins/trello/impl/impl.go
+++ b/backend/plugins/trello/impl/impl.go
@@ -46,7 +46,8 @@ var _ interface {
 type Trello struct{}
 
 func (p Trello) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
@@ -66,6 +67,10 @@ func (p Trello) Description() string {
        return "To collect and enrich data from Trello"
 }
 
+func (p Trello) Name() string {
+       return "trello"
+}
+
 func (p Trello) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.CollectListMeta,
@@ -105,6 +110,7 @@ func (p Trello) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        err = connectionHelper.FirstById(connection, op.ConnectionId)
 
@@ -129,14 +135,18 @@ func (p Trello) MigrationScripts() 
[]plugin.MigrationScript {
        return migrationscripts.All()
 }
 
-func (p Trello) Connection() interface{} {
+func (p Trello) Connection() dal.Tabler {
        return &models.TrelloConnection{}
 }
 
-func (p Trello) Scope() interface{} {
+func (p Trello) Scope() plugin.ToolLayerScope {
        return &models.TrelloBoard{}
 }
 
+func (p Trello) ScopeConfig() dal.Tabler {
+       return &models.TrelloScopeConfig{}
+}
+
 func (p Trello) ApiResources() map[string]map[string]plugin.ApiResourceHandler 
{
        return map[string]map[string]plugin.ApiResourceHandler{
                "test": {
diff --git a/backend/plugins/trello/models/board.go 
b/backend/plugins/trello/models/board.go
index bc54d8ebe..400e5444a 100644
--- a/backend/plugins/trello/models/board.go
+++ b/backend/plugins/trello/models/board.go
@@ -19,6 +19,7 @@ package models
 
 import (
        "github.com/apache/incubator-devlake/core/models/common"
+       "github.com/apache/incubator-devlake/core/plugin"
 )
 
 type TrelloBoard struct {
@@ -29,6 +30,16 @@ type TrelloBoard struct {
        Name             string `json:"name" mapstructure:"name" 
gorm:"type:varchar(255)"`
 }
 
+func (b TrelloBoard) ScopeId() string {
+       return b.BoardId
+}
+
+func (b TrelloBoard) ScopeName() string {
+       return b.Name
+}
+
 func (TrelloBoard) TableName() string {
        return "_tool_trello_boards"
 }
+
+var _ plugin.ToolLayerScope = (*TrelloBoard)(nil)
diff --git a/backend/plugins/webhook/api/connection.go 
b/backend/plugins/webhook/api/connection.go
index fea459af8..650f67e42 100644
--- a/backend/plugins/webhook/api/connection.go
+++ b/backend/plugins/webhook/api/connection.go
@@ -80,7 +80,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/webhook/api/init.go 
b/backend/plugins/webhook/api/init.go
index d92c2b334..1da6ff414 100644
--- a/backend/plugins/webhook/api/init.go
+++ b/backend/plugins/webhook/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/go-playground/validator/v10"
 )
@@ -27,11 +28,13 @@ var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
 var basicRes context.BasicRes
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
 }
diff --git a/backend/plugins/webhook/impl/impl.go 
b/backend/plugins/webhook/impl/impl.go
index 3dcff3d43..395d5c550 100644
--- a/backend/plugins/webhook/impl/impl.go
+++ b/backend/plugins/webhook/impl/impl.go
@@ -27,11 +27,13 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Webhook)(nil)
-var _ plugin.PluginInit = (*Webhook)(nil)
-var _ plugin.PluginApi = (*Webhook)(nil)
-var _ plugin.PluginModel = (*Webhook)(nil)
-var _ plugin.PluginMigration = (*Webhook)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginApi
+       plugin.PluginModel
+       plugin.PluginMigration
+} = (*Webhook)(nil)
 
 type Webhook struct{}
 
@@ -39,8 +41,13 @@ func (p Webhook) Description() string {
        return "collect some Webhook data"
 }
 
+func (p Webhook) Name() string {
+       return "webhook"
+}
+
 func (p Webhook) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
diff --git a/backend/plugins/zentao/api/blueprint_V200_test.go 
b/backend/plugins/zentao/api/blueprint_V200_test.go
index 42432f76b..64d93be67 100644
--- a/backend/plugins/zentao/api/blueprint_V200_test.go
+++ b/backend/plugins/zentao/api/blueprint_V200_test.go
@@ -55,10 +55,12 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
        }
        mockMeta := mockplugin.NewPluginMeta(t)
        
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/zentao")
+       mockMeta.On("Name").Return("zentao").Maybe()
        err := plugin.RegisterPlugin("zentao", mockMeta)
        assert.Nil(t, err)
        // Refresh Global Variables and set the sql mock
-       mockBasicRes()
+       mockBasicRes(t)
+
        bs := &plugin.BlueprintScopeV200{
                Id: "project/1",
        }
@@ -125,7 +127,7 @@ func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
 }
 
 // mockBasicRes FIXME ...
-func mockBasicRes() {
+func mockBasicRes(t *testing.T) {
        testZentaoProduct := &models.ZentaoProduct{
                ConnectionId:  1,
                Id:            1,
@@ -155,5 +157,7 @@ func mockBasicRes() {
                        panic("The empty scope should not call First() for 
ZentaoScopeConfig")
                }).Return(nil)
        })
-       Init(mockRes)
+       p := mockplugin.NewPluginMeta(t)
+       p.On("Name").Return("dummy").Maybe()
+       Init(mockRes, p)
 }
diff --git a/backend/plugins/zentao/api/blueprint_v200.go 
b/backend/plugins/zentao/api/blueprint_v200.go
index 3aa7b449d..350b4b35e 100644
--- a/backend/plugins/zentao/api/blueprint_v200.go
+++ b/backend/plugins/zentao/api/blueprint_v200.go
@@ -30,11 +30,9 @@ import (
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
        "github.com/apache/incubator-devlake/plugins/zentao/tasks"
-       "github.com/go-playground/validator/v10"
 )
 
 func MakeDataSourcePipelinePlanV200(subtaskMetas []plugin.SubTaskMeta, 
connectionId uint64, bpScopes []*plugin.BlueprintScopeV200, syncPolicy 
*plugin.BlueprintSyncPolicy) (plugin.PipelinePlan, []plugin.Scope, 
errors.Error) {
-       connectionHelper := helper.NewConnectionHelper(basicRes, 
validator.New())
        // get the connection info for url
        connection := &models.ZentaoConnection{}
        err := connectionHelper.FirstById(connection, connectionId)
diff --git a/backend/plugins/zentao/api/connection.go 
b/backend/plugins/zentao/api/connection.go
index 429cd4587..e34e747aa 100644
--- a/backend/plugins/zentao/api/connection.go
+++ b/backend/plugins/zentao/api/connection.go
@@ -114,7 +114,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput
                return nil, err
        }
        var refs *services.BlueprintProjectPairs
-       refs, err = connectionHelper.Delete(input.GetPlugin(), connection)
+       refs, err = connectionHelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/plugins/zentao/api/init.go 
b/backend/plugins/zentao/api/init.go
index 7695bf784..20d4b68cb 100644
--- a/backend/plugins/zentao/api/init.go
+++ b/backend/plugins/zentao/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
        "github.com/go-playground/validator/v10"
@@ -39,12 +40,14 @@ var projectRemoteHelper 
*api.RemoteApiHelper[models.ZentaoConnection, models.Zen
 var basicRes context.BasicRes
 var scHelper *api.ScopeConfigHelper[models.ZentaoScopeConfig]
 
-func Init(br context.BasicRes) {
+func Init(br context.BasicRes, p plugin.PluginMeta) {
+
        basicRes = br
        vld = validator.New()
        connectionHelper = api.NewConnectionHelper(
                basicRes,
                vld,
+               p.Name(),
        )
        productParams := &api.ReflectionParameters{
                ScopeIdFieldName:  "Id",
diff --git a/backend/plugins/zentao/impl/impl.go 
b/backend/plugins/zentao/impl/impl.go
index cb90a71f6..6370d5264 100644
--- a/backend/plugins/zentao/impl/impl.go
+++ b/backend/plugins/zentao/impl/impl.go
@@ -35,13 +35,17 @@ import (
 )
 
 // make sure interface is implemented
-var _ plugin.PluginMeta = (*Zentao)(nil)
-var _ plugin.PluginInit = (*Zentao)(nil)
-var _ plugin.PluginTask = (*Zentao)(nil)
-var _ plugin.PluginApi = (*Zentao)(nil)
 
-// var _ plugin.CompositePluginBlueprintV200 = (*Zentao)(nil)
-var _ plugin.CloseablePluginTask = (*Zentao)(nil)
+var _ interface {
+       plugin.PluginMeta
+       plugin.PluginInit
+       plugin.PluginTask
+       plugin.PluginApi
+       //plugin.CompositePluginBlueprintV200
+       plugin.PluginModel
+       plugin.PluginSource
+       plugin.CloseablePluginTask
+} = (*Zentao)(nil)
 
 type Zentao struct{}
 
@@ -49,11 +53,51 @@ func (p Zentao) Description() string {
        return "collect some Zentao data"
 }
 
+func (p Zentao) Name() string {
+       return "zentao"
+}
+
 func (p Zentao) Init(basicRes context.BasicRes) errors.Error {
-       api.Init(basicRes)
+       api.Init(basicRes, p)
+
        return nil
 }
 
+func (p Zentao) GetTablesInfo() []dal.Tabler {
+       return []dal.Tabler{
+               &models.ZentaoAccount{},
+               &models.ZentaoBug{},
+               &models.ZentaoBugCommit{},
+               &models.ZentaoChangelog{},
+               &models.ZentaoChangelogDetail{},
+               &models.ZentaoDepartment{},
+               &models.ZentaoExecution{},
+               &models.ZentaoProduct{},
+               &models.ZentaoProject{},
+               &models.ZentaoRemoteDbAction{},
+               &models.ZentaoRemoteDbActionHistory{},
+               &models.ZentaoRemoteDbHistory{},
+               &models.ZentaoStory{},
+               &models.ZentaoStoryCommit{},
+               &models.ZentaoStoryRepoCommit{},
+               &models.ZentaoTask{},
+               &models.ZentaoTaskCommit{},
+               &models.ZentaoTaskRepoCommit{},
+       }
+}
+
+func (p Zentao) Connection() dal.Tabler {
+       return &models.ZentaoConnection{}
+}
+
+func (p Zentao) Scope() plugin.ToolLayerScope {
+       return &models.ZentaoProject{}
+}
+
+func (p Zentao) ScopeConfig() dal.Tabler {
+       return &models.ZentaoScopeConfig{}
+}
+
 func (p Zentao) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.ConvertProductMeta,
@@ -114,6 +158,7 @@ func (p Zentao) PrepareTaskData(taskCtx plugin.TaskContext, 
options map[string]i
        connectionHelper := helper.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
        connection := &models.ZentaoConnection{}
        err = connectionHelper.FirstById(connection, op.ConnectionId)
diff --git a/backend/server/services/remote/models/conversion.go 
b/backend/server/services/remote/models/conversion.go
index affbd0b17..51c2e55e2 100644
--- a/backend/server/services/remote/models/conversion.go
+++ b/backend/server/services/remote/models/conversion.go
@@ -32,7 +32,7 @@ import (
        "gorm.io/datatypes"
 )
 
-func LoadTableModel(tableName string, schema utils.JsonObject, parentModel 
any) (*models.DynamicTabler, errors.Error) {
+func LoadTableModel(tableName string, schema utils.JsonObject, parentModel 
any) (models.DynamicTabler, errors.Error) {
        structType, err := GenerateStructType(schema, 
reflect.TypeOf(parentModel))
        if err != nil {
                return nil, err
diff --git a/backend/server/services/remote/models/models.go 
b/backend/server/services/remote/models/models.go
index 2868703aa..23bcb6877 100644
--- a/backend/server/services/remote/models/models.go
+++ b/backend/server/services/remote/models/models.go
@@ -22,6 +22,7 @@ import (
        "github.com/apache/incubator-devlake/core/models"
        "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/plugin"
+       "reflect"
 )
 
 type PluginExtension string
@@ -57,7 +58,7 @@ type DynamicModelInfo struct {
        TableName  string         `json:"table_name" validate:"required"`
 }
 
-func (d DynamicModelInfo) LoadDynamicTabler(parentModel any) 
(*models.DynamicTabler, errors.Error) {
+func (d DynamicModelInfo) LoadDynamicTabler(parentModel any) 
(models.DynamicTabler, errors.Error) {
        return LoadTableModel(d.TableName, d.JsonSchema, parentModel)
 }
 
@@ -69,6 +70,24 @@ type ScopeModel struct {
        ScopeConfigId    uint64 `json:"scopeConfigId"`
 }
 
+type DynamicScopeModel struct {
+       models.DynamicTabler
+}
+
+func NewDynamicScopeModel(model models.DynamicTabler) *DynamicScopeModel {
+       return &DynamicScopeModel{
+               DynamicTabler: model.New(),
+       }
+}
+
+func (d *DynamicScopeModel) ScopeId() string {
+       return 
reflect.ValueOf(d.DynamicTabler.Unwrap()).Elem().FieldByName("Id").String()
+}
+
+func (d *DynamicScopeModel) ScopeName() string {
+       return 
reflect.ValueOf(d.DynamicTabler.Unwrap()).Elem().FieldByName("Name").String()
+}
+
 type ScopeConfigModel struct {
        common.ScopeConfig `mapstructure:",squash"`
        ConnectionId       uint64 `json:"connectionId"`
@@ -94,3 +113,5 @@ type PipelineData struct {
        Plan   plugin.PipelinePlan  `json:"plan"`
        Scopes []DynamicDomainScope `json:"scopes"`
 }
+
+var _ plugin.ToolLayerScope = (*DynamicScopeModel)(nil)
diff --git a/backend/server/services/remote/models/plugin_remote.go 
b/backend/server/services/remote/models/plugin_remote.go
index 1d0c0f57b..358d89a96 100644
--- a/backend/server/services/remote/models/plugin_remote.go
+++ b/backend/server/services/remote/models/plugin_remote.go
@@ -30,5 +30,6 @@ type RemotePlugin interface {
        plugin.PluginOpenApiSpec
        plugin.PluginModel
        plugin.PluginMigration
+       plugin.PluginSource
        RunAutoMigrations() errors.Error
 }
diff --git a/backend/server/services/remote/plugin/connection_api.go 
b/backend/server/services/remote/plugin/connection_api.go
index 4a4929aae..0d380e3f2 100644
--- a/backend/server/services/remote/plugin/connection_api.go
+++ b/backend/server/services/remote/plugin/connection_api.go
@@ -53,7 +53,7 @@ func (pa *pluginAPI) TestConnection(input 
*plugin.ApiResourceInput) (*plugin.Api
 
 func (pa *pluginAPI) PostConnections(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        connection := pa.connType.New()
-       err := pa.helper.Create(connection, input)
+       err := pa.connhelper.Create(connection, input)
        if err != nil {
                return nil, err
        }
@@ -63,7 +63,7 @@ func (pa *pluginAPI) PostConnections(input 
*plugin.ApiResourceInput) (*plugin.Ap
 
 func (pa *pluginAPI) ListConnections(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        connections := pa.connType.NewSlice()
-       err := pa.helper.List(connections)
+       err := pa.connhelper.List(connections)
        if err != nil {
                return nil, err
        }
@@ -73,7 +73,7 @@ func (pa *pluginAPI) ListConnections(input 
*plugin.ApiResourceInput) (*plugin.Ap
 
 func (pa *pluginAPI) GetConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        connection := pa.connType.New()
-       err := pa.helper.First(connection, input.Params)
+       err := pa.connhelper.First(connection, input.Params)
        if err != nil {
                return nil, err
        }
@@ -83,7 +83,7 @@ func (pa *pluginAPI) GetConnection(input 
*plugin.ApiResourceInput) (*plugin.ApiR
 
 func (pa *pluginAPI) PatchConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        connection := pa.connType.New()
-       err := pa.helper.Patch(connection, input)
+       err := pa.connhelper.Patch(connection, input)
        if err != nil {
                return nil, err
        }
@@ -93,11 +93,11 @@ func (pa *pluginAPI) PatchConnection(input 
*plugin.ApiResourceInput) (*plugin.Ap
 
 func (pa *pluginAPI) DeleteConnection(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
        connection := pa.connType.New()
-       err := pa.helper.First(connection, input.Params)
+       err := pa.connhelper.First(connection, input.Params)
        if err != nil {
                return nil, err
        }
-       refs, err := pa.helper.Delete(input.GetPlugin(), connection)
+       refs, err := pa.connhelper.Delete(connection)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, err
        }
diff --git a/backend/server/services/remote/plugin/default_api.go 
b/backend/server/services/remote/plugin/default_api.go
index eac7cdea8..68e788c2d 100644
--- a/backend/server/services/remote/plugin/default_api.go
+++ b/backend/server/services/remote/plugin/default_api.go
@@ -27,27 +27,27 @@ import (
 
 type pluginAPI struct {
        invoker         bridge.Invoker
-       connType        *models.DynamicTabler
-       scopeType       *models.DynamicTabler
-       scopeConfigType *models.DynamicTabler
-       helper          *api.ConnectionApiHelper
+       connType        models.DynamicTabler
+       scopeType       models.DynamicTabler
+       scopeConfigType models.DynamicTabler
+       connhelper      *api.ConnectionApiHelper
+       scopeHelper     
*api.GenericScopeApiHelper[remoteModel.RemoteConnection, 
remoteModel.RemoteScope, remoteModel.RemoteScopeConfig]
 }
 
 func GetDefaultAPI(
        invoker bridge.Invoker,
-       connType *models.DynamicTabler,
-       scopeConfigType *models.DynamicTabler,
-       scopeType *models.DynamicTabler,
-       helper *api.ConnectionApiHelper,
+       connType models.DynamicTabler,
+       scopeConfigType models.DynamicTabler,
+       scopeType models.DynamicTabler,
+       connHelper *api.ConnectionApiHelper,
 ) map[string]map[string]plugin.ApiResourceHandler {
        papi := &pluginAPI{
                invoker:         invoker,
                connType:        connType,
                scopeConfigType: scopeConfigType,
                scopeType:       scopeType,
-               helper:          helper,
+               connhelper:      connHelper,
        }
-
        resources := map[string]map[string]plugin.ApiResourceHandler{
                "test": {
                        "POST": papi.TestConnection,
@@ -86,21 +86,20 @@ func GetDefaultAPI(
                        "GET": papi.SearchRemoteScopes,
                },
        }
-
-       scopeHelper = createScopeHelper(papi)
+       papi.createScopeHelper()
        return resources
 }
 
-func createScopeHelper(pa *pluginAPI) 
*api.GenericScopeApiHelper[remoteModel.RemoteConnection, 
remoteModel.RemoteScope, remoteModel.RemoteScopeConfig] {
+func (pa *pluginAPI) createScopeHelper() {
        params := &api.ReflectionParameters{
                ScopeIdFieldName:  "Id",
                ScopeIdColumnName: "id",
                RawScopeParamName: "scope_id",
        }
-       return api.NewGenericScopeHelper[remoteModel.RemoteConnection, 
remoteModel.RemoteScope, remoteModel.RemoteScopeConfig](
+       pa.scopeHelper = 
api.NewGenericScopeHelper[remoteModel.RemoteConnection, 
remoteModel.RemoteScope, remoteModel.RemoteScopeConfig](
                basicRes,
                vld,
-               connectionHelper,
+               pa.connhelper,
                NewScopeDatabaseHelperImpl(pa, basicRes, params),
                params,
                &api.ScopeHelperOptions{
diff --git a/backend/server/services/remote/plugin/init.go 
b/backend/server/services/remote/plugin/init.go
index 699547c4d..12a2fa9a8 100644
--- a/backend/server/services/remote/plugin/init.go
+++ b/backend/server/services/remote/plugin/init.go
@@ -20,26 +20,19 @@ package plugin
 import (
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
-       "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/server/services/remote/bridge"
        "github.com/apache/incubator-devlake/server/services/remote/models"
        "github.com/go-playground/validator/v10"
 )
 
 var (
-       connectionHelper *api.ConnectionApiHelper
-       scopeHelper      *api.GenericScopeApiHelper[models.RemoteConnection, 
models.RemoteScope, models.RemoteScopeConfig]
-       basicRes         context.BasicRes
-       vld              *validator.Validate
+       basicRes context.BasicRes
+       vld      *validator.Validate
 )
 
 func Init(br context.BasicRes) {
        vld = validator.New()
        basicRes = br
-       connectionHelper = api.NewConnectionHelper(
-               br,
-               vld,
-       )
 }
 
 func NewRemotePlugin(info *models.PluginInfo) (models.RemotePlugin, 
errors.Error) {
diff --git a/backend/server/services/remote/plugin/plugin_extensions.go 
b/backend/server/services/remote/plugin/plugin_extensions.go
index c51f79a3a..f4d9225d1 100644
--- a/backend/server/services/remote/plugin/plugin_extensions.go
+++ b/backend/server/services/remote/plugin/plugin_extensions.go
@@ -41,7 +41,7 @@ func (p remoteMetricPlugin) 
MakeMetricPluginPipelinePlanV200(projectName string,
 
 func (p remoteDatasourcePlugin) MakeDataSourcePipelinePlanV200(connectionId 
uint64, bpScopes []*plugin.BlueprintScopeV200, syncPolicy 
plugin.BlueprintSyncPolicy) (plugin.PipelinePlan, []plugin.Scope, errors.Error) 
{
        connection := p.connectionTabler.New()
-       err := connectionHelper.FirstById(connection, connectionId)
+       err := p.connHelper.FirstById(connection, connectionId)
        if err != nil {
                return nil, nil, err
        }
diff --git a/backend/server/services/remote/plugin/plugin_impl.go 
b/backend/server/services/remote/plugin/plugin_impl.go
index f07f86388..d6a033794 100644
--- a/backend/server/services/remote/plugin/plugin_impl.go
+++ b/backend/server/services/remote/plugin/plugin_impl.go
@@ -38,13 +38,14 @@ type (
                pluginPath        string
                description       string
                invoker           bridge.Invoker
-               connectionTabler  *coreModels.DynamicTabler
-               scopeTabler       *coreModels.DynamicTabler
-               scopeConfigTabler *coreModels.DynamicTabler
-               toolModelTablers  []*coreModels.DynamicTabler
+               connectionTabler  coreModels.DynamicTabler
+               scopeTabler       coreModels.DynamicTabler
+               scopeConfigTabler coreModels.DynamicTabler
+               toolModelTablers  []coreModels.DynamicTabler
                migrationScripts  []plugin.MigrationScript
                resources         
map[string]map[string]plugin.ApiResourceHandler
                openApiSpec       string
+               connHelper        *api.ConnectionApiHelper
        }
        RemotePluginTaskData struct {
                DbUrl       string                 `json:"db_url"`
@@ -69,9 +70,9 @@ func newPlugin(info *models.PluginInfo, invoker 
bridge.Invoker) (*remotePluginIm
                return nil, errors.Default.Wrap(err, fmt.Sprintf("Couldn't load 
ScopeConfig type for plugin %s", info.Name))
        }
        // put the scope and connection models in the tool list to be 
consistent with Go plugins
-       toolModelTablers := []*coreModels.DynamicTabler{
+       toolModelTablers := []coreModels.DynamicTabler{
                connectionTabler.New(),
-               scopeTabler.New(),
+               models.NewDynamicScopeModel(scopeTabler),
        }
        for _, toolModelInfo := range info.ToolModelInfos {
                toolModelTabler, err := 
toolModelInfo.LoadDynamicTabler(common.NoPKModel{})
@@ -89,6 +90,12 @@ func newPlugin(info *models.PluginInfo, invoker 
bridge.Invoker) (*remotePluginIm
                script := script
                scripts = append(scripts, &script)
        }
+       connectionHelper := api.NewConnectionHelper(
+               basicRes,
+               vld,
+               info.Name,
+       )
+       apiResources := GetDefaultAPI(invoker, connectionTabler, 
scopeConfigTabler, scopeTabler, connectionHelper)
        p := remotePluginImpl{
                name:              info.Name,
                invoker:           invoker,
@@ -99,8 +106,9 @@ func newPlugin(info *models.PluginInfo, invoker 
bridge.Invoker) (*remotePluginIm
                scopeConfigTabler: scopeConfigTabler,
                toolModelTablers:  toolModelTablers,
                migrationScripts:  scripts,
-               resources:         GetDefaultAPI(invoker, connectionTabler, 
scopeConfigTabler, scopeTabler, connectionHelper),
+               resources:         apiResources,
                openApiSpec:       *openApiSpec,
+               connHelper:        connectionHelper,
        }
        remoteBridge := bridge.NewBridge(invoker)
        for _, subtask := range info.SubtaskMetas {
@@ -128,6 +136,18 @@ func (p *remotePluginImpl) GetTablesInfo() []dal.Tabler {
        return tables
 }
 
+func (p *remotePluginImpl) Connection() dal.Tabler {
+       return p.connectionTabler.New()
+}
+
+func (p *remotePluginImpl) Scope() plugin.ToolLayerScope {
+       return models.NewDynamicScopeModel(p.scopeTabler)
+}
+
+func (p *remotePluginImpl) ScopeConfig() dal.Tabler {
+       return p.scopeConfigTabler.New()
+}
+
 func (p *remotePluginImpl) PrepareTaskData(taskCtx plugin.TaskContext, options 
map[string]interface{}) (interface{}, errors.Error) {
        dbUrl := taskCtx.GetConfig("db_url")
        connectionId := uint64(options["connectionId"].(float64))
@@ -135,6 +155,7 @@ func (p *remotePluginImpl) PrepareTaskData(taskCtx 
plugin.TaskContext, options m
        helper := api.NewConnectionHelper(
                taskCtx,
                nil,
+               p.Name(),
        )
 
        wrappedConnection := p.connectionTabler.New()
@@ -191,6 +212,10 @@ func (p *remotePluginImpl) Description() string {
        return p.description
 }
 
+func (p *remotePluginImpl) Name() string {
+       return p.name
+}
+
 func (p *remotePluginImpl) RootPkgPath() string {
        // RootPkgPath is used by DomainIdGenerator to find the name of the 
plugin that defines a given type.
        // While remote plugins do not use the DomainIdGenerator, we still need 
to implement this function.
diff --git a/backend/server/services/remote/plugin/remote_scope_api.go 
b/backend/server/services/remote/plugin/remote_scope_api.go
index f40458739..e766a2c8b 100644
--- a/backend/server/services/remote/plugin/remote_scope_api.go
+++ b/backend/server/services/remote/plugin/remote_scope_api.go
@@ -45,7 +45,7 @@ func (pa *pluginAPI) GetRemoteScopes(input 
*plugin.ApiResourceInput) (*plugin.Ap
        }
 
        connection := pa.connType.New()
-       err := pa.helper.First(connection, input.Params)
+       err := pa.connhelper.First(connection, input.Params)
        if err != nil {
                return nil, err
        }
diff --git a/backend/server/services/remote/plugin/scope_api.go 
b/backend/server/services/remote/plugin/scope_api.go
index 1f65bd84c..01731d1af 100644
--- a/backend/server/services/remote/plugin/scope_api.go
+++ b/backend/server/services/remote/plugin/scope_api.go
@@ -47,7 +47,7 @@ func (pa *pluginAPI) PutScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResour
                }
                slice = append(slice, &obj)
        }
-       apiScopes, err := scopeHelper.PutScopes(input, slice)
+       apiScopes, err := pa.scopeHelper.PutScopes(input, slice)
        if err != nil {
                return nil, err
        }
@@ -59,7 +59,7 @@ func (pa *pluginAPI) PutScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResour
 }
 
 func (pa *pluginAPI) UpdateScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
-       apiScopes, err := scopeHelper.UpdateScope(input)
+       apiScopes, err := pa.scopeHelper.UpdateScope(input)
        if err != nil {
                return nil, err
        }
@@ -71,7 +71,7 @@ func (pa *pluginAPI) UpdateScope(input 
*plugin.ApiResourceInput) (*plugin.ApiRes
 }
 
 func (pa *pluginAPI) ListScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
-       scopes, err := scopeHelper.GetScopes(input)
+       scopes, err := pa.scopeHelper.GetScopes(input)
        if err != nil {
                return nil, err
        }
@@ -83,7 +83,7 @@ func (pa *pluginAPI) ListScopes(input 
*plugin.ApiResourceInput) (*plugin.ApiReso
 }
 
 func (pa *pluginAPI) GetScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
-       scope, err := scopeHelper.GetScope(input)
+       scope, err := pa.scopeHelper.GetScope(input)
        if err != nil {
                return nil, err
        }
@@ -95,7 +95,7 @@ func (pa *pluginAPI) GetScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResour
 }
 
 func (pa *pluginAPI) DeleteScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
-       refs, err := scopeHelper.DeleteScope(input)
+       refs, err := pa.scopeHelper.DeleteScope(input)
        if err != nil {
                return &plugin.ApiResourceOutput{Body: refs, Status: 
err.GetType().GetHttpCode()}, nil
        }
diff --git a/backend/server/services/remote/plugin/scope_db_helper.go 
b/backend/server/services/remote/plugin/scope_db_helper.go
index 4d063593f..2a424b678 100644
--- a/backend/server/services/remote/plugin/scope_db_helper.go
+++ b/backend/server/services/remote/plugin/scope_db_helper.go
@@ -43,7 +43,7 @@ func NewScopeDatabaseHelperImpl(pa *pluginAPI, basicRes 
context.BasicRes, params
                pa:         pa,
                db:         basicRes.GetDal(),
                params:     params,
-               connHelper: connectionHelper,
+               connHelper: pa.connhelper,
        }
 }
 
diff --git a/backend/test/e2e/remote/python_plugin_test.go 
b/backend/test/e2e/remote/python_plugin_test.go
index 6e64923ac..3b0bbfc80 100644
--- a/backend/test/e2e/remote/python_plugin_test.go
+++ b/backend/test/e2e/remote/python_plugin_test.go
@@ -52,13 +52,23 @@ func TestDeleteConnection(t *testing.T) {
 
 func TestDeleteConnection_Conflict(t *testing.T) {
        client := CreateClient(t)
-       _ = CreateTestBlueprints(t, client, 1)
+       params := CreateTestBlueprints(t, client, 1)
        conns := client.ListConnections(PLUGIN_NAME)
        require.Equal(t, 1, len(conns))
        require.Equal(t, TOKEN, conns[0].Token)
        refs := 
client.SetExpectedStatusCode(http.StatusConflict).DeleteConnection(PLUGIN_NAME, 
conns[0].ID)
        require.Equal(t, 1, len(refs.Projects))
        require.Equal(t, 1, len(refs.Blueprints))
+       client.DeleteBlueprint(params.blueprints[0].ID)
+       refs = 
client.SetExpectedStatusCode(http.StatusConflict).DeleteConnection(PLUGIN_NAME, 
conns[0].ID)
+       // should still conflict because we have scopes tied to this connection
+       require.Equal(t, 0, len(refs.Projects))
+       require.Equal(t, 0, len(refs.Blueprints))
+       client.DeleteScope(PLUGIN_NAME, params.connection.ID, params.scope.Id, 
false)
+       refs = client.DeleteConnection(PLUGIN_NAME, conns[0].ID)
+       require.Equal(t, 0, len(refs.Projects))
+       require.Equal(t, 0, len(refs.Blueprints))
+
 }
 
 func TestRemoteScopeGroups(t *testing.T) {
@@ -108,18 +118,20 @@ func TestCreateScope(t *testing.T) {
        conn := CreateTestConnection(client)
        scopeConfig := CreateTestScopeConfig(client, conn.ID)
        scope := CreateTestScope(client, scopeConfig, conn.ID)
-
        scopes := client.ListScopes(PLUGIN_NAME, conn.ID, false)
        require.Equal(t, 1, len(scopes))
-
-       cicdScope := helper.Cast[FakeProject](scopes[0].Scope)
+       cicdScope := helper.Cast[FakeProject](client.GetScope(PLUGIN_NAME, 
conn.ID, scope.Id, false).Scope)
+       require.Equal(t, scope.Id, cicdScope.Id)
+       cicdScope0 := helper.Cast[FakeProject](scopes[0].Scope)
+       require.Equal(t, scope.Id, cicdScope0.Id)
        require.Equal(t, conn.ID, cicdScope.ConnectionId)
        require.Equal(t, "p1", cicdScope.Id)
        require.Equal(t, "Project 1", cicdScope.Name)
        require.Equal(t, "http://fake.org/api/project/p1";, cicdScope.Url)
-
        cicdScope.Name = "scope-name-2"
-       client.UpdateScope(PLUGIN_NAME, conn.ID, cicdScope.Id, scope)
+       client.UpdateScope(PLUGIN_NAME, conn.ID, cicdScope.Id, cicdScope)
+       cicdScope = helper.Cast[FakeProject](client.GetScope(PLUGIN_NAME, 
conn.ID, scope.Id, false).Scope)
+       require.Equal(t, "scope-name-2", cicdScope.Name)
 }
 
 func TestRunPipeline(t *testing.T) {
diff --git a/backend/test/helper/api.go b/backend/test/helper/api.go
index 022458e2f..be6dfd069 100644
--- a/backend/test/helper/api.go
+++ b/backend/test/helper/api.go
@@ -27,7 +27,6 @@ import (
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models"
        "github.com/apache/incubator-devlake/core/plugin"
-       "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/server/api/blueprints"
        apiProject "github.com/apache/incubator-devlake/server/api/project"
        "github.com/stretchr/testify/require"
@@ -220,12 +219,13 @@ func (d *DevlakeClient) ListScopes(pluginName string, 
connectionId uint64, listB
        return responses
 }
 
-func (d *DevlakeClient) GetScope(pluginName string, connectionId uint64, 
scopeId string, listBlueprints bool) any {
-       return sendHttpRequest[api.ScopeRes[any, any]](d.testCtx, d.timeout, 
&testContext{
+func (d *DevlakeClient) GetScope(pluginName string, connectionId uint64, 
scopeId string, listBlueprints bool) ScopeResponse {
+       scopeRaw := sendHttpRequest[map[string]any](d.testCtx, d.timeout, 
&testContext{
                client:       d,
                printPayload: true,
                inlineJson:   false,
        }, http.MethodGet, 
fmt.Sprintf("%s/plugins/%s/connections/%d/scopes/%s?blueprints=%v", d.Endpoint, 
pluginName, connectionId, scopeId, listBlueprints), nil, nil)
+       return getScopeResponse(scopeRaw)
 }
 
 func (d *DevlakeClient) DeleteScope(pluginName string, connectionId uint64, 
scopeId string, deleteDataOnly bool) services.BlueprintProjectPairs {

Reply via email to