You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by ab...@apache.org on 2024/04/17 09:04:54 UTC

(incubator-devlake) branch main updated: refactor: opsgenie adopts the new dshelpers (#7341)

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

abeizn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 62b67c1fe refactor: opsgenie adopts the new dshelpers (#7341)
62b67c1fe is described below

commit 62b67c1fe67868a873c5db0ca57e7ebc2b453af7
Author: Klesh Wong <zh...@merico.dev>
AuthorDate: Wed Apr 17 17:04:46 2024 +0800

    refactor: opsgenie adopts the new dshelpers (#7341)
---
 .../pluginhelper/api/ds_scope_api_helper.go        |   3 +
 .../pluginhelper/api/scope_generic_helper.go       |   1 -
 backend/helpers/srvhelper/model_service_helper.go  |   1 -
 .../api/{scope.go => scope_api.go}                 |  14 +-
 .../api/{blueprint200.go => blueprint_v200.go}     |   0
 backend/plugins/circleci/tasks/task_data.go        |   6 +-
 backend/plugins/opsgenie/api/blueprint_v200.go     |  22 +--
 .../api/{connection.go => connection_api.go}       |  41 +----
 backend/plugins/opsgenie/api/init.go               |  16 +-
 .../opsgenie/api/{remote.go => remote_api.go}      | 166 ++++++---------------
 .../opsgenie/api/{scope.go => scope_api.go}        |  16 +-
 11 files changed, 80 insertions(+), 206 deletions(-)

diff --git a/backend/helpers/pluginhelper/api/ds_scope_api_helper.go b/backend/helpers/pluginhelper/api/ds_scope_api_helper.go
index 3775f19e3..eb0b8af51 100644
--- a/backend/helpers/pluginhelper/api/ds_scope_api_helper.go
+++ b/backend/helpers/pluginhelper/api/ds_scope_api_helper.go
@@ -24,10 +24,13 @@ import (
 	"github.com/apache/incubator-devlake/core/errors"
 	"github.com/apache/incubator-devlake/core/models/common"
 	"github.com/apache/incubator-devlake/core/plugin"
+	serviceHelper "github.com/apache/incubator-devlake/helpers/pluginhelper/services"
 	"github.com/apache/incubator-devlake/helpers/srvhelper"
 	"github.com/apache/incubator-devlake/server/api/shared"
 )
 
+type ScopeRefDoc = serviceHelper.BlueprintProjectPairs
+
 type PutScopesReqBody[T any] struct {
 	Data []*T `json:"data"`
 }
diff --git a/backend/helpers/pluginhelper/api/scope_generic_helper.go b/backend/helpers/pluginhelper/api/scope_generic_helper.go
index 1fe90fae9..a782b4e09 100644
--- a/backend/helpers/pluginhelper/api/scope_generic_helper.go
+++ b/backend/helpers/pluginhelper/api/scope_generic_helper.go
@@ -60,7 +60,6 @@ type (
 		Blueprints  []*models.Blueprint `mapstructure:"blueprints,omitempty" json:"blueprints"`
 	}
 	// Alias, for swagger purposes
-	ScopeRefDoc                                            = serviceHelper.BlueprintProjectPairs
 	ScopeRes[Scope plugin.ToolLayerScope, ScopeConfig any] struct {
 		Scope       Scope               `mapstructure:"scope,omitempty" json:"scope,omitempty"`
 		ScopeConfig *ScopeConfig        `mapstructure:"scopeConfig,omitempty" json:"scopeConfig,omitempty"`
diff --git a/backend/helpers/srvhelper/model_service_helper.go b/backend/helpers/srvhelper/model_service_helper.go
index 9f040d1a7..3ec9abe39 100644
--- a/backend/helpers/srvhelper/model_service_helper.go
+++ b/backend/helpers/srvhelper/model_service_helper.go
@@ -96,7 +96,6 @@ func (srv *ModelSrvHelper[M]) ValidateModel(model *M) errors.Error {
 
 // Create validates given model and insert it into database if validation passed
 func (srv *ModelSrvHelper[M]) Create(model *M) errors.Error {
-	println("create model")
 	err := srv.ValidateModel(model)
 	if err != nil {
 		return err
diff --git a/backend/plugins/bitbucket_server/api/scope.go b/backend/plugins/bitbucket_server/api/scope_api.go
similarity index 94%
rename from backend/plugins/bitbucket_server/api/scope.go
rename to backend/plugins/bitbucket_server/api/scope_api.go
index 3b98ceeec..442566d76 100644
--- a/backend/plugins/bitbucket_server/api/scope.go
+++ b/backend/plugins/bitbucket_server/api/scope_api.go
@@ -26,12 +26,8 @@ import (
 	"github.com/apache/incubator-devlake/plugins/bitbucket_server/models"
 )
 
-type ScopeRes struct {
-	models.BitbucketServerRepo
-	api.ScopeResDoc[models.BitbucketServerScopeConfig]
-}
-
-type ScopeReq api.ScopeReq[models.BitbucketServerRepo]
+type PutScopesReqBody api.PutScopesReqBody[models.BitbucketServerRepo]
+type ScopeDetail api.ScopeDetail[models.BitbucketServerRepo, models.BitbucketServerScopeConfig]
 
 // PutScope create or update repo
 // @Summary create or update repo
@@ -39,7 +35,7 @@ type ScopeReq api.ScopeReq[models.BitbucketServerRepo]
 // @Tags plugins/bitbucket_server
 // @Accept application/json
 // @Param connectionId path int true "connection ID"
-// @Param scope body ScopeReq true "json"
+// @Param scope body PutScopesReqBody true "json"
 // @Success 200  {object} []models.BitbucketServerRepo
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
@@ -74,7 +70,7 @@ func UpdateScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, err
 // @Param pageSize query int false "page size, default 50"
 // @Param page query int false "page size, default 1"
 // @Param blueprints query bool false "also return blueprints using these scopes as part of the payload"
-// @Success 200  {object} []ScopeRes
+// @Success 200  {object} []ScopeDetail
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
 // @Router /plugins/bitbucket_server/connections/{connectionId}/scopes/ [GET]
@@ -97,7 +93,7 @@ func GetScopeDispatcher(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutp
 // @Tags plugins/bitbucket_server
 // @Param connectionId path int true "connection ID"
 // @Param scopeId path string true "repo ID"
-// @Success 200  {object} ScopeRes
+// @Success 200  {object} ScopeDetail
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
 // @Router /plugins/bitbucket_server/connections/{connectionId}/scopes/{scopeId} [GET]
diff --git a/backend/plugins/circleci/api/blueprint200.go b/backend/plugins/circleci/api/blueprint_v200.go
similarity index 100%
rename from backend/plugins/circleci/api/blueprint200.go
rename to backend/plugins/circleci/api/blueprint_v200.go
diff --git a/backend/plugins/circleci/tasks/task_data.go b/backend/plugins/circleci/tasks/task_data.go
index fafccd952..960325696 100644
--- a/backend/plugins/circleci/tasks/task_data.go
+++ b/backend/plugins/circleci/tasks/task_data.go
@@ -26,9 +26,9 @@ import (
 type CircleciOptions struct {
 	ConnectionId  uint64                      `json:"connectionId" mapstructure:"connectionId"`
 	ProjectSlug   string                      `json:"projectSlug" mapstructure:"projectSlug"`
-	PageSize      uint64                      `mapstruct:"pageSize" mapstructure:"pageSize,omitempty"`
-	ScopeConfigId uint64                      `json:"scopeConfigId" mapstructure:"scopeConfigId,omitempty"`
-	ScopeConfig   *models.CircleciScopeConfig `json:"scopeConfig" mapstructure:"scopeConfig,omitempty"`
+	PageSize      uint64                      `json:"pageSize,omitempty" mapstructure:"pageSize,omitempty"`
+	ScopeConfigId uint64                      `json:"scopeConfigId,omitempty" mapstructure:"scopeConfigId,omitempty"`
+	ScopeConfig   *models.CircleciScopeConfig `json:"scopeConfig,omitempty" mapstructure:"scopeConfig,omitempty"`
 }
 
 type CircleciTaskData struct {
diff --git a/backend/plugins/opsgenie/api/blueprint_v200.go b/backend/plugins/opsgenie/api/blueprint_v200.go
index 3f691ddba..3b34e7fef 100644
--- a/backend/plugins/opsgenie/api/blueprint_v200.go
+++ b/backend/plugins/opsgenie/api/blueprint_v200.go
@@ -35,9 +35,7 @@ func MakeDataSourcePipelinePlanV200(
 	connectionId uint64,
 	bpScopes []*coreModels.BlueprintScope,
 ) (coreModels.PipelinePlan, []plugin.Scope, errors.Error) {
-	// get the connection info for url
-	connection := &models.OpsgenieConnection{}
-	err := connectionHelper.FirstById(connection, connectionId)
+	connection, err := dsHelper.ConnSrv.FindByPk(connectionId)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -45,20 +43,15 @@ func MakeDataSourcePipelinePlanV200(
 	if err != nil {
 		return nil, nil, err
 	}
-
-	plan, err := makeDataSourcePipelinePlanV200(subtaskMetas, scopeDetails, connection)
+	plan, err := makePipelinePlanV200(subtaskMetas, scopeDetails, connection)
 	if err != nil {
 		return nil, nil, err
 	}
 	scopes, err := makeScopesV200(scopeDetails, connection)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	return plan, scopes, nil
+	return plan, scopes, err
 }
 
-func makeDataSourcePipelinePlanV200(
+func makePipelinePlanV200(
 	subtaskMetas []plugin.SubTaskMeta,
 	scopeDetails []*srvhelper.ScopeDetail[models.Service, models.OpsenieScopeConfig],
 	connection *models.OpsgenieConnection,
@@ -77,7 +70,7 @@ func makeDataSourcePipelinePlanV200(
 			subtaskMetas,
 			scopeConfig.Entities,
 			OpsgenieTaskOptions{
-				ConnectionId: scope.ConnectionId,
+				ConnectionId: connection.ID,
 				ServiceId:    scope.Id,
 				ServiceName:  scope.Name,
 			},
@@ -95,7 +88,8 @@ func makeDataSourcePipelinePlanV200(
 
 func makeScopesV200(
 	scopeDetails []*srvhelper.ScopeDetail[models.Service, models.OpsenieScopeConfig],
-	connection *models.OpsgenieConnection) ([]plugin.Scope, errors.Error) {
+	connection *models.OpsgenieConnection,
+) ([]plugin.Scope, errors.Error) {
 	scopes := make([]plugin.Scope, 0)
 	for _, scopeDetail := range scopeDetails {
 		opService, scopeConfig := scopeDetail.Scope, scopeDetail.ScopeConfig
@@ -103,7 +97,7 @@ func makeScopesV200(
 		if utils.StringsContains(scopeConfig.Entities, plugin.DOMAIN_TYPE_TICKET) {
 			domainBoard := &ticket.Board{
 				DomainEntity: domainlayer.DomainEntity{
-					Id: didgen.NewDomainIdGenerator(&models.Service{}).Generate(opService.ConnectionId, opService.Id),
+					Id: didgen.NewDomainIdGenerator(&models.Service{}).Generate(connection.ID, opService.Id),
 				},
 				Name: opService.Name,
 			}
diff --git a/backend/plugins/opsgenie/api/connection.go b/backend/plugins/opsgenie/api/connection_api.go
similarity index 83%
rename from backend/plugins/opsgenie/api/connection.go
rename to backend/plugins/opsgenie/api/connection_api.go
index 77f4989e3..c2844efaa 100644
--- a/backend/plugins/opsgenie/api/connection.go
+++ b/backend/plugins/opsgenie/api/connection_api.go
@@ -68,8 +68,7 @@ func testOpsgenieConn(ctx context.Context, connection models.OpsgenieConn) (*plu
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId}/test [POST]
 func TestExistingConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	connection := &models.OpsgenieConnection{}
-	err := connectionHelper.First(connection, input.Params)
+	connection, err := dsHelper.ConnApi.GetMergedConnection(input)
 	if err != nil {
 		return nil, err
 	}
@@ -114,12 +113,7 @@ func TestConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/opsgenie/connections [POST]
 func PostConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	connection := &models.OpsgenieConnection{}
-	err := connectionHelper.Create(connection, input)
-	if err != nil {
-		return nil, err
-	}
-	return &plugin.ApiResourceOutput{Body: connection.Sanitize(), Status: http.StatusOK}, nil
+	return dsHelper.ConnApi.Post(input)
 }
 
 // @Summary patch opsgenie connection
@@ -131,17 +125,7 @@ func PostConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId} [PATCH]
 func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	connection := &models.OpsgenieConnection{}
-	if err := connectionHelper.First(&connection, input.Params); err != nil {
-		return nil, err
-	}
-	if err := (&models.OpsgenieConnection{}).MergeFromRequest(connection, input.Body); err != nil {
-		return nil, errors.Convert(err)
-	}
-	if err := connectionHelper.SaveWithCreateOrUpdate(connection); err != nil {
-		return nil, err
-	}
-	return &plugin.ApiResourceOutput{Body: connection.Sanitize(), Status: http.StatusOK}, nil
+	return dsHelper.ConnApi.Patch(input)
 }
 
 // @Summary delete opsgenie connection
@@ -153,7 +137,7 @@ func PatchConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId} [DELETE]
 func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	return connectionHelper.Delete(&models.OpsgenieConnection{}, input)
+	return dsHelper.ConnApi.Delete(input)
 }
 
 // @Summary list opsgenie connections
@@ -164,15 +148,7 @@ func DeleteConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/opsgenie/connections [GET]
 func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	var connections []models.OpsgenieConnection
-	err := connectionHelper.List(&connections)
-	if err != nil {
-		return nil, err
-	}
-	for idx, c := range connections {
-		connections[idx] = c.Sanitize()
-	}
-	return &plugin.ApiResourceOutput{Body: connections}, nil
+	return dsHelper.ConnApi.GetAll(input)
 }
 
 // @Summary get opsgenie connection
@@ -183,10 +159,5 @@ func ListConnections(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
 // @Failure 500  {string} errcode.Error "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId} [GET]
 func GetConnection(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	connection := &models.OpsgenieConnection{}
-	err := connectionHelper.First(connection, input.Params)
-	if err != nil {
-		return nil, err
-	}
-	return &plugin.ApiResourceOutput{Body: connection.Sanitize()}, nil
+	return dsHelper.ConnApi.GetDetail(input)
 }
diff --git a/backend/plugins/opsgenie/api/init.go b/backend/plugins/opsgenie/api/init.go
index 3641eab40..c72ee736f 100644
--- a/backend/plugins/opsgenie/api/init.go
+++ b/backend/plugins/opsgenie/api/init.go
@@ -26,20 +26,15 @@ import (
 )
 
 var vld *validator.Validate
-var connectionHelper *api.ConnectionApiHelper
-var dsHelper *api.DsHelper[models.OpsgenieConnection, models.Service, models.OpsenieScopeConfig]
+
 var basicRes context.BasicRes
+var dsHelper *api.DsHelper[models.OpsgenieConnection, models.Service, models.OpsenieScopeConfig]
+var raProxy *api.DsRemoteApiProxyHelper[models.OpsgenieConnection]
+var raScopeList *api.DsRemoteApiScopeListHelper[models.OpsgenieConnection, models.Service, OpsgenieRemotePagination]
 
 func Init(br context.BasicRes, p plugin.PluginMeta) {
-
 	basicRes = br
 	vld = validator.New()
-	connectionHelper = api.NewConnectionHelper(
-		basicRes,
-		vld,
-		p.Name(),
-	)
-
 	dsHelper = api.NewDataSourceHelper[
 		models.OpsgenieConnection, models.Service, models.OpsenieScopeConfig,
 	](
@@ -52,5 +47,6 @@ func Init(br context.BasicRes, p plugin.PluginMeta) {
 		nil,
 		nil,
 	)
-
+	raProxy = api.NewDsRemoteApiProxyHelper[models.OpsgenieConnection](dsHelper.ConnApi.ModelApiHelper)
+	raScopeList = api.NewDsRemoteApiScopeListHelper[models.OpsgenieConnection, models.Service, OpsgenieRemotePagination](raProxy, listOpsgenieRemoteScopes)
 }
diff --git a/backend/plugins/opsgenie/api/remote.go b/backend/plugins/opsgenie/api/remote_api.go
similarity index 51%
rename from backend/plugins/opsgenie/api/remote.go
rename to backend/plugins/opsgenie/api/remote_api.go
index b6b1780b4..bc3e1999f 100644
--- a/backend/plugins/opsgenie/api/remote.go
+++ b/backend/plugins/opsgenie/api/remote_api.go
@@ -18,43 +18,20 @@ limitations under the License.
 package api
 
 import (
-	"context"
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"net/http"
 	"net/url"
-	"strconv"
 
 	"github.com/apache/incubator-devlake/core/errors"
 	"github.com/apache/incubator-devlake/core/models/common"
 	"github.com/apache/incubator-devlake/core/plugin"
 	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	dsmodels "github.com/apache/incubator-devlake/helpers/pluginhelper/api/models"
 	"github.com/apache/incubator-devlake/plugins/opsgenie/models"
 	"github.com/apache/incubator-devlake/plugins/opsgenie/models/raw"
 )
 
-type RemoteScopesChild struct {
-	Type     string      `json:"type"`
-	ParentId *string     `json:"parentId"`
-	Id       string      `json:"id"`
-	Name     string      `json:"name"`
-	FullName string      `json:"fullName"`
-	Data     interface{} `json:"data"`
-}
-
-type RemoteScopesOutput struct {
-	Children      []RemoteScopesChild `json:"children"`
-	NextPageToken string              `json:"nextPageToken"`
-}
-
-type SearchRemoteScopesOutput struct {
-	Children []RemoteScopesChild `json:"children"`
-	Page     int                 `json:"page"`
-	PageSize int                 `json:"pageSize"`
-}
-
-type PageData struct {
+type OpsgenieRemotePagination struct {
 	Page    int `json:"page"`
 	PerPage int `json:"per_page"`
 }
@@ -64,74 +41,46 @@ type ServiceResponse struct {
 	Data       []raw.Service `json:"data"`
 }
 
-const RemoteScopesPerPage int = 100
-const TypeScope string = "scope"
-
-// RemoteScopes list all available scopes (services) for this connection
-// @Summary list all available scopes (services) for this connection
-// @Description list all available scopes (services) for this connection
-// @Tags plugins/opsgenie
-// @Accept application/json
-// @Param connectionId path int false "connection ID"
-// @Param groupId query string false "group ID"
-// @Param pageToken query string false "page Token"
-// @Success 200  {object} RemoteScopesOutput
-// @Failure 400  {object} shared.ApiBody "Bad Request"
-// @Failure 500  {object} shared.ApiBody "Internal Error"
-// @Router /plugins/opsgenie/connections/{connectionId}/remote-scopes [GET]
-func RemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
-	connectionId, _ := extractParam(input.Params)
-	if connectionId == 0 {
-		return nil, errors.BadInput.New("invalid connectionId")
+func listOpsgenieRemoteScopes(
+	connection *models.OpsgenieConnection,
+	apiClient plugin.ApiClient,
+	groupId string,
+	page OpsgenieRemotePagination,
+) (
+	children []dsmodels.DsRemoteApiScopeListEntry[models.Service],
+	nextPage *OpsgenieRemotePagination,
+	err errors.Error,
+) {
+	if page.Page == 0 {
+		page.Page = 1
 	}
-
-	connection := &models.OpsgenieConnection{}
-	err := connectionHelper.First(connection, input.Params)
-	if err != nil {
-		return nil, err
-	}
-
-	pageToken, ok := input.Query["pageToken"]
-	if !ok || len(pageToken) == 0 {
-		pageToken = []string{""}
+	if page.PerPage == 0 {
+		page.PerPage = 100
 	}
 
-	pageData, err := DecodeFromPageToken(pageToken[0])
-	if err != nil {
-		return nil, errors.BadInput.New("failed to get page token")
-	}
-
-	// create api client
-	apiClient, err := api.NewApiClientFromConnection(context.TODO(), basicRes, connection)
-	if err != nil {
-		return nil, err
-	}
-
-	query, err := GetQueryFromPageData(pageData)
-	if err != nil {
-		return nil, err
+	query := url.Values{
+		"page":     []string{fmt.Sprintf("%v", page.Page)},
+		"per_page": []string{fmt.Sprintf("%v", page.PerPage)},
 	}
 
-	var res *http.Response
-	outputBody := &RemoteScopesOutput{}
-	res, err = apiClient.Get("v1/services", query, nil)
+	res, err := apiClient.Get("v1/services", query, nil)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 	response := &ServiceResponse{}
 	err = api.UnmarshalResponse(res, response)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
 	// append service to output
 	for _, service := range response.Data {
-		child := RemoteScopesChild{
-			Type:     TypeScope,
+		children = append(children, dsmodels.DsRemoteApiScopeListEntry[models.Service]{
+			Type:     api.RAS_ENTRY_TYPE_SCOPE,
 			Id:       service.Id,
 			Name:     service.Name,
 			FullName: service.Name,
-			Data: models.Service{
+			Data: &models.Service{
 				Url:    service.Links.Web,
 				Id:     service.Id,
 				Name:   service.Name,
@@ -141,11 +90,26 @@ func RemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, er
 					ConnectionId: connection.ID,
 				},
 			},
-		}
-		outputBody.Children = append(outputBody.Children, child)
+		})
 	}
 
-	return &plugin.ApiResourceOutput{Body: outputBody, Status: http.StatusOK}, nil
+	return
+}
+
+// RemoteScopes list all available scopes (services) for this connection
+// @Summary list all available scopes (services) for this connection
+// @Description list all available scopes (services) for this connection
+// @Tags plugins/opsgenie
+// @Accept application/json
+// @Param connectionId path int false "connection ID"
+// @Param groupId query string false "group ID"
+// @Param pageToken query string false "page Token"
+// @Success 200  {object} RemoteScopesOutput
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/opsgenie/connections/{connectionId}/remote-scopes [GET]
+func RemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return raScopeList.Get(input)
 }
 
 // SearchRemoteScopes use the Search API and only return project
@@ -165,47 +129,3 @@ func SearchRemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutp
 	// Not supported
 	return &plugin.ApiResourceOutput{Body: nil, Status: http.StatusMethodNotAllowed}, nil
 }
-
-func EncodeToPageToken(pageData *PageData) (string, errors.Error) {
-	// Marshal json
-	pageTokenDecode, err := json.Marshal(pageData)
-	if err != nil {
-		return "", errors.Default.Wrap(err, fmt.Sprintf("Marshal pageToken failed %+v", pageData))
-	}
-	// Encode pageToken Base64
-	return base64.StdEncoding.EncodeToString(pageTokenDecode), nil
-}
-
-func DecodeFromPageToken(pageToken string) (*PageData, errors.Error) {
-	if pageToken == "" {
-		return &PageData{
-			Page:    0,
-			PerPage: RemoteScopesPerPage,
-		}, nil
-	}
-	// Decode pageToken Base64
-	pageTokenDecode, err := base64.StdEncoding.DecodeString(pageToken)
-	if err != nil {
-		return nil, errors.Default.Wrap(err, fmt.Sprintf("decode pageToken failed %s", pageToken))
-	}
-	// Unmarshal json
-	pt := &PageData{}
-	err = json.Unmarshal(pageTokenDecode, pt)
-	if err != nil {
-		return nil, errors.Default.Wrap(err, fmt.Sprintf("json Unmarshal pageTokenDecode failed %s", pageTokenDecode))
-	}
-	return pt, nil
-}
-
-func GetQueryFromPageData(pageData *PageData) (url.Values, errors.Error) {
-	query := url.Values{}
-	query.Set("offset", fmt.Sprintf("%v", pageData.Page*pageData.PerPage))
-	query.Set("limit", fmt.Sprintf("%v", pageData.PerPage))
-	return query, nil
-}
-
-func extractParam(params map[string]string) (uint64, uint64) {
-	connectionId, _ := strconv.ParseUint(params["connectionId"], 10, 64)
-	serviceId, _ := strconv.ParseUint(params["serviceId"], 10, 64)
-	return connectionId, serviceId
-}
diff --git a/backend/plugins/opsgenie/api/scope.go b/backend/plugins/opsgenie/api/scope_api.go
similarity index 93%
rename from backend/plugins/opsgenie/api/scope.go
rename to backend/plugins/opsgenie/api/scope_api.go
index 4e0cb2f76..572e75d29 100644
--- a/backend/plugins/opsgenie/api/scope.go
+++ b/backend/plugins/opsgenie/api/scope_api.go
@@ -24,12 +24,8 @@ import (
 	"github.com/apache/incubator-devlake/plugins/opsgenie/models"
 )
 
-type ScopeRes struct {
-	models.Service
-	api.ScopeResDoc[models.Service]
-}
-
-type ScopeReq api.ScopeReq[models.Service]
+type PutScopesReqBody api.PutScopesReqBody[models.Service]
+type ScopeDetail api.ScopeDetail[models.Service, models.OpsenieScopeConfig]
 
 // PutScope create or update opsgenie service
 // @Summary create or update opsgenie service
@@ -37,8 +33,8 @@ type ScopeReq api.ScopeReq[models.Service]
 // @Tags plugins/opsgenie
 // @Accept application/json
 // @Param connectionId path int true "connection ID"
-// @Param scope body ScopeReq true "json"
-// @Success 200  {object} []ScopeRes
+// @Param scope body PutScopesReqBody true "json"
+// @Success 200  {object} []models.Service
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId}/scopes [PUT]
@@ -71,7 +67,7 @@ func UpdateScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, err
 // @Param pageSize query int false "page size, default 50"
 // @Param page query int false "page size, default 1"
 // @Param blueprints query bool false "also return blueprints using these scopes as part of the payload"
-// @Success 200  {object} []ScopeRes
+// @Success 200  {object} []ScopeDetail
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId}/scopes/ [GET]
@@ -86,7 +82,7 @@ func GetScopeList(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, er
 // @Param connectionId path int true "connection ID"
 // @Param serviceId path int true "service ID"
 // @Param blueprints query bool false "also return blueprints using this scope as part of the payload"
-// @Success 200  {object} ScopeRes
+// @Success 200  {object} ScopeDetail
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
 // @Router /plugins/opsgenie/connections/{connectionId}/scopes/{serviceId} [GET]