401 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			401 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
/*
 | 
						|
 * Copyright 2025 coze-dev Authors
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
package service
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/bytedance/sonic"
 | 
						|
	"github.com/getkin/kin-openapi/openapi3"
 | 
						|
 | 
						|
	model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
 | 
						|
	common "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop_common"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/pkg/errorx"
 | 
						|
	"github.com/coze-dev/coze-studio/backend/types/errno"
 | 
						|
)
 | 
						|
 | 
						|
//go:generate mockgen -destination ../../../internal/mock/domain/plugin/interface.go --package mockPlugin -source service.go
 | 
						|
type PluginService interface {
 | 
						|
	// Draft Plugin
 | 
						|
	CreateDraftPlugin(ctx context.Context, req *CreateDraftPluginRequest) (pluginID int64, err error)
 | 
						|
	CreateDraftPluginWithCode(ctx context.Context, req *CreateDraftPluginWithCodeRequest) (resp *CreateDraftPluginWithCodeResponse, err error)
 | 
						|
	GetDraftPlugin(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, err error)
 | 
						|
	MGetDraftPlugins(ctx context.Context, pluginIDs []int64) (plugins []*entity.PluginInfo, err error)
 | 
						|
	ListDraftPlugins(ctx context.Context, req *ListDraftPluginsRequest) (resp *ListDraftPluginsResponse, err error)
 | 
						|
	UpdateDraftPlugin(ctx context.Context, plugin *UpdateDraftPluginRequest) (err error)
 | 
						|
	UpdateDraftPluginWithCode(ctx context.Context, req *UpdateDraftPluginWithCodeRequest) (err error)
 | 
						|
	DeleteDraftPlugin(ctx context.Context, pluginID int64) (err error)
 | 
						|
	DeleteAPPAllPlugins(ctx context.Context, appID int64) (pluginIDs []int64, err error)
 | 
						|
	GetAPPAllPlugins(ctx context.Context, appID int64) (plugins []*entity.PluginInfo, err error)
 | 
						|
 | 
						|
	// Online Plugin
 | 
						|
	PublishPlugin(ctx context.Context, req *PublishPluginRequest) (err error)
 | 
						|
	PublishAPPPlugins(ctx context.Context, req *PublishAPPPluginsRequest) (resp *PublishAPPPluginsResponse, err error)
 | 
						|
	GetOnlinePlugin(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, err error)
 | 
						|
	MGetOnlinePlugins(ctx context.Context, pluginIDs []int64) (plugins []*entity.PluginInfo, err error)
 | 
						|
	MGetPluginLatestVersion(ctx context.Context, pluginIDs []int64) (resp *MGetPluginLatestVersionResponse, err error)
 | 
						|
	GetPluginNextVersion(ctx context.Context, pluginID int64) (version string, err error)
 | 
						|
	MGetVersionPlugins(ctx context.Context, versionPlugins []entity.VersionPlugin) (plugins []*entity.PluginInfo, err error)
 | 
						|
	ListCustomOnlinePlugins(ctx context.Context, spaceID int64, pageInfo entity.PageInfo) (plugins []*entity.PluginInfo, total int64, err error)
 | 
						|
 | 
						|
	// Draft Tool
 | 
						|
	MGetDraftTools(ctx context.Context, toolIDs []int64) (tools []*entity.ToolInfo, err error)
 | 
						|
	UpdateDraftTool(ctx context.Context, req *UpdateDraftToolRequest) (err error)
 | 
						|
	ConvertToOpenapi3Doc(ctx context.Context, req *ConvertToOpenapi3DocRequest) (resp *ConvertToOpenapi3DocResponse)
 | 
						|
	CreateDraftToolsWithCode(ctx context.Context, req *CreateDraftToolsWithCodeRequest) (resp *CreateDraftToolsWithCodeResponse, err error)
 | 
						|
	CheckPluginToolsDebugStatus(ctx context.Context, pluginID int64) (err error)
 | 
						|
 | 
						|
	// Online Tool
 | 
						|
	GetOnlineTool(ctx context.Context, toolID int64) (tool *entity.ToolInfo, err error)
 | 
						|
	MGetOnlineTools(ctx context.Context, toolIDs []int64) (tools []*entity.ToolInfo, err error)
 | 
						|
	MGetVersionTools(ctx context.Context, versionTools []entity.VersionTool) (tools []*entity.ToolInfo, err error)
 | 
						|
	CopyPlugin(ctx context.Context, req *CopyPluginRequest) (resp *CopyPluginResponse, err error)
 | 
						|
	MoveAPPPluginToLibrary(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, err error)
 | 
						|
 | 
						|
	// Agent Tool
 | 
						|
	BindAgentTools(ctx context.Context, agentID int64, toolIDs []int64) (err error)
 | 
						|
	DuplicateDraftAgentTools(ctx context.Context, fromAgentID, toAgentID int64) (err error)
 | 
						|
	GetDraftAgentToolByName(ctx context.Context, agentID int64, toolName string) (tool *entity.ToolInfo, err error)
 | 
						|
	MGetAgentTools(ctx context.Context, req *MGetAgentToolsRequest) (tools []*entity.ToolInfo, err error)
 | 
						|
	UpdateBotDefaultParams(ctx context.Context, req *UpdateBotDefaultParamsRequest) (err error)
 | 
						|
 | 
						|
	PublishAgentTools(ctx context.Context, agentID int64, agentVersion string) (err error)
 | 
						|
 | 
						|
	ExecuteTool(ctx context.Context, req *ExecuteToolRequest, opts ...entity.ExecuteToolOpt) (resp *ExecuteToolResponse, err error)
 | 
						|
 | 
						|
	// Product
 | 
						|
	ListPluginProducts(ctx context.Context, req *ListPluginProductsRequest) (resp *ListPluginProductsResponse, err error)
 | 
						|
	GetPluginProductAllTools(ctx context.Context, pluginID int64) (tools []*entity.ToolInfo, err error)
 | 
						|
 | 
						|
	GetOAuthStatus(ctx context.Context, userID, pluginID int64) (resp *GetOAuthStatusResponse, err error)
 | 
						|
	GetAgentPluginsOAuthStatus(ctx context.Context, userID, agentID int64) (status []*AgentPluginOAuthStatus, err error)
 | 
						|
	OAuthCode(ctx context.Context, code string, state *entity.OAuthState) (err error)
 | 
						|
	GetAccessToken(ctx context.Context, oa *entity.OAuthInfo) (accessToken string, err error)
 | 
						|
	RevokeAccessToken(ctx context.Context, meta *entity.AuthorizationCodeMeta) (err error)
 | 
						|
}
 | 
						|
 | 
						|
type CreateDraftPluginRequest struct {
 | 
						|
	PluginType   common.PluginType
 | 
						|
	IconURI      string
 | 
						|
	SpaceID      int64
 | 
						|
	DeveloperID  int64
 | 
						|
	ProjectID    *int64
 | 
						|
	Name         string
 | 
						|
	Desc         string
 | 
						|
	ServerURL    string
 | 
						|
	CommonParams map[common.ParameterLocation][]*common.CommonParamSchema
 | 
						|
	AuthInfo     *PluginAuthInfo
 | 
						|
}
 | 
						|
 | 
						|
type UpdateDraftPluginWithCodeRequest struct {
 | 
						|
	UserID     int64
 | 
						|
	PluginID   int64
 | 
						|
	OpenapiDoc *model.Openapi3T
 | 
						|
	Manifest   *entity.PluginManifest
 | 
						|
}
 | 
						|
 | 
						|
type UpdateDraftPluginRequest struct {
 | 
						|
	PluginID     int64
 | 
						|
	Name         *string
 | 
						|
	Desc         *string
 | 
						|
	URL          *string
 | 
						|
	Icon         *common.PluginIcon
 | 
						|
	CommonParams map[common.ParameterLocation][]*common.CommonParamSchema
 | 
						|
	AuthInfo     *PluginAuthInfo
 | 
						|
}
 | 
						|
 | 
						|
type ListDraftPluginsRequest struct {
 | 
						|
	SpaceID  int64
 | 
						|
	APPID    int64
 | 
						|
	PageInfo entity.PageInfo
 | 
						|
}
 | 
						|
 | 
						|
type ListDraftPluginsResponse struct {
 | 
						|
	Plugins []*entity.PluginInfo
 | 
						|
	Total   int64
 | 
						|
}
 | 
						|
 | 
						|
type CreateDraftPluginWithCodeRequest struct {
 | 
						|
	SpaceID     int64
 | 
						|
	DeveloperID int64
 | 
						|
	ProjectID   *int64
 | 
						|
	Manifest    *entity.PluginManifest
 | 
						|
	OpenapiDoc  *model.Openapi3T
 | 
						|
}
 | 
						|
 | 
						|
type CreateDraftPluginWithCodeResponse struct {
 | 
						|
	Plugin *entity.PluginInfo
 | 
						|
	Tools  []*entity.ToolInfo
 | 
						|
}
 | 
						|
 | 
						|
type CreateDraftToolsWithCodeRequest struct {
 | 
						|
	PluginID   int64
 | 
						|
	OpenapiDoc *model.Openapi3T
 | 
						|
 | 
						|
	ConflictAndUpdate bool
 | 
						|
}
 | 
						|
 | 
						|
type CreateDraftToolsWithCodeResponse struct {
 | 
						|
	DuplicatedTools []entity.UniqueToolAPI
 | 
						|
}
 | 
						|
 | 
						|
type PluginAuthInfo struct {
 | 
						|
	AuthzType    *model.AuthzType
 | 
						|
	Location     *model.HTTPParamLocation
 | 
						|
	Key          *string
 | 
						|
	ServiceToken *string
 | 
						|
	OAuthInfo    *string
 | 
						|
	AuthzSubType *model.AuthzSubType
 | 
						|
	AuthzPayload *string
 | 
						|
}
 | 
						|
 | 
						|
func (p PluginAuthInfo) toAuthV2() (*model.AuthV2, error) {
 | 
						|
	if p.AuthzType == nil {
 | 
						|
		return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KV(errno.PluginMsgKey, "auth type is required"))
 | 
						|
	}
 | 
						|
 | 
						|
	switch *p.AuthzType {
 | 
						|
	case model.AuthzTypeOfNone:
 | 
						|
		return &model.AuthV2{
 | 
						|
			Type: model.AuthzTypeOfNone,
 | 
						|
		}, nil
 | 
						|
 | 
						|
	case model.AuthzTypeOfOAuth:
 | 
						|
		m, err := p.authOfOAuthToAuthV2()
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return m, nil
 | 
						|
 | 
						|
	case model.AuthzTypeOfService:
 | 
						|
		m, err := p.authOfServiceToAuthV2()
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return m, nil
 | 
						|
 | 
						|
	default:
 | 
						|
		return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KVf(errno.PluginMsgKey,
 | 
						|
			"the type '%s' of auth is invalid", *p.AuthzType))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (p PluginAuthInfo) authOfOAuthToAuthV2() (*model.AuthV2, error) {
 | 
						|
	if p.AuthzSubType == nil {
 | 
						|
		return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KV(errno.PluginMsgKey, "sub-auth type is required"))
 | 
						|
	}
 | 
						|
 | 
						|
	if p.OAuthInfo == nil || *p.OAuthInfo == "" {
 | 
						|
		return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KV(errno.PluginMsgKey, "oauth info is required"))
 | 
						|
	}
 | 
						|
 | 
						|
	oauthInfo := make(map[string]string)
 | 
						|
	err := sonic.Unmarshal([]byte(*p.OAuthInfo), &oauthInfo)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errorx.WrapByCode(err, errno.ErrPluginInvalidManifest, errorx.KV(errno.PluginMsgKey, "invalid oauth info"))
 | 
						|
	}
 | 
						|
 | 
						|
	if *p.AuthzSubType == model.AuthzSubTypeOfOAuthClientCredentials {
 | 
						|
		_oauthInfo := &model.OAuthClientCredentialsConfig{
 | 
						|
			ClientID:     oauthInfo["client_id"],
 | 
						|
			ClientSecret: oauthInfo["client_secret"],
 | 
						|
			TokenURL:     oauthInfo["token_url"],
 | 
						|
		}
 | 
						|
 | 
						|
		str, err := sonic.MarshalString(_oauthInfo)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("marshal oauth info failed, err=%v", err)
 | 
						|
		}
 | 
						|
 | 
						|
		return &model.AuthV2{
 | 
						|
			Type:                         model.AuthzTypeOfOAuth,
 | 
						|
			SubType:                      model.AuthzSubTypeOfOAuthClientCredentials,
 | 
						|
			Payload:                      str,
 | 
						|
			AuthOfOAuthClientCredentials: _oauthInfo,
 | 
						|
		}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if *p.AuthzSubType == model.AuthzSubTypeOfOAuthAuthorizationCode {
 | 
						|
		contentType := oauthInfo["authorization_content_type"]
 | 
						|
		if contentType != model.MediaTypeJson { // only support application/json
 | 
						|
			return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KVf(errno.PluginMsgKey,
 | 
						|
				"the type '%s' of authorization content is invalid", contentType))
 | 
						|
		}
 | 
						|
 | 
						|
		_oauthInfo := &model.OAuthAuthorizationCodeConfig{
 | 
						|
			ClientID:                 oauthInfo["client_id"],
 | 
						|
			ClientSecret:             oauthInfo["client_secret"],
 | 
						|
			ClientURL:                oauthInfo["client_url"],
 | 
						|
			Scope:                    oauthInfo["scope"],
 | 
						|
			AuthorizationURL:         oauthInfo["authorization_url"],
 | 
						|
			AuthorizationContentType: contentType,
 | 
						|
		}
 | 
						|
 | 
						|
		str, err := sonic.MarshalString(_oauthInfo)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("marshal oauth info failed, err=%v", err)
 | 
						|
		}
 | 
						|
 | 
						|
		return &model.AuthV2{
 | 
						|
			Type:                         model.AuthzTypeOfOAuth,
 | 
						|
			SubType:                      model.AuthzSubTypeOfOAuthAuthorizationCode,
 | 
						|
			Payload:                      str,
 | 
						|
			AuthOfOAuthAuthorizationCode: _oauthInfo,
 | 
						|
		}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KVf(errno.PluginMsgKey,
 | 
						|
		"the type '%s' of sub-auth is invalid", *p.AuthzSubType))
 | 
						|
}
 | 
						|
 | 
						|
func (p PluginAuthInfo) authOfServiceToAuthV2() (*model.AuthV2, error) {
 | 
						|
	if p.AuthzSubType == nil {
 | 
						|
		return nil, fmt.Errorf("sub-auth type is required")
 | 
						|
	}
 | 
						|
 | 
						|
	if *p.AuthzSubType == model.AuthzSubTypeOfServiceAPIToken {
 | 
						|
		if p.Location == nil {
 | 
						|
			return nil, fmt.Errorf("'Location' of sub-auth is required")
 | 
						|
		}
 | 
						|
		if p.ServiceToken == nil {
 | 
						|
			return nil, fmt.Errorf("'ServiceToken' of sub-auth is required")
 | 
						|
		}
 | 
						|
		if p.Key == nil {
 | 
						|
			return nil, fmt.Errorf("'Key' of sub-auth is required")
 | 
						|
		}
 | 
						|
 | 
						|
		tokenAuth := &model.AuthOfAPIToken{
 | 
						|
			ServiceToken: *p.ServiceToken,
 | 
						|
			Location:     model.HTTPParamLocation(strings.ToLower(string(*p.Location))),
 | 
						|
			Key:          *p.Key,
 | 
						|
		}
 | 
						|
 | 
						|
		str, err := sonic.MarshalString(tokenAuth)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("marshal token auth failed, err=%v", err)
 | 
						|
		}
 | 
						|
 | 
						|
		return &model.AuthV2{
 | 
						|
			Type:           model.AuthzTypeOfService,
 | 
						|
			SubType:        model.AuthzSubTypeOfServiceAPIToken,
 | 
						|
			Payload:        str,
 | 
						|
			AuthOfAPIToken: tokenAuth,
 | 
						|
		}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, errorx.New(errno.ErrPluginInvalidManifest, errorx.KVf(errno.PluginMsgKey,
 | 
						|
		"the type '%s' of sub-auth is invalid", *p.AuthzSubType))
 | 
						|
}
 | 
						|
 | 
						|
type PublishPluginRequest = model.PublishPluginRequest
 | 
						|
 | 
						|
type PublishAPPPluginsRequest = model.PublishAPPPluginsRequest
 | 
						|
 | 
						|
type PublishAPPPluginsResponse = model.PublishAPPPluginsResponse
 | 
						|
 | 
						|
type MGetPluginLatestVersionResponse = model.MGetPluginLatestVersionResponse
 | 
						|
 | 
						|
type UpdateDraftToolRequest struct {
 | 
						|
	PluginID     int64
 | 
						|
	ToolID       int64
 | 
						|
	Name         *string
 | 
						|
	Desc         *string
 | 
						|
	SubURL       *string
 | 
						|
	Method       *string
 | 
						|
	Parameters   openapi3.Parameters
 | 
						|
	RequestBody  *openapi3.RequestBodyRef
 | 
						|
	Responses    openapi3.Responses
 | 
						|
	Disabled     *bool
 | 
						|
	SaveExample  *bool
 | 
						|
	DebugExample *common.DebugExample
 | 
						|
	APIExtend    *common.APIExtend
 | 
						|
}
 | 
						|
 | 
						|
type MGetAgentToolsRequest = model.MGetAgentToolsRequest
 | 
						|
 | 
						|
type UpdateBotDefaultParamsRequest struct {
 | 
						|
	PluginID    int64
 | 
						|
	AgentID     int64
 | 
						|
	ToolName    string
 | 
						|
	Parameters  openapi3.Parameters
 | 
						|
	RequestBody *openapi3.RequestBodyRef
 | 
						|
	Responses   openapi3.Responses
 | 
						|
}
 | 
						|
 | 
						|
type ExecuteToolRequest = model.ExecuteToolRequest
 | 
						|
 | 
						|
type ExecuteToolResponse = model.ExecuteToolResponse
 | 
						|
 | 
						|
type ListPluginProductsRequest struct{}
 | 
						|
 | 
						|
type ListPluginProductsResponse struct {
 | 
						|
	Plugins []*entity.PluginInfo
 | 
						|
	Total   int64
 | 
						|
}
 | 
						|
 | 
						|
type ConvertToOpenapi3DocRequest struct {
 | 
						|
	RawInput        string
 | 
						|
	PluginServerURL *string
 | 
						|
}
 | 
						|
 | 
						|
type ConvertToOpenapi3DocResponse struct {
 | 
						|
	OpenapiDoc *model.Openapi3T
 | 
						|
	Manifest   *entity.PluginManifest
 | 
						|
	Format     common.PluginDataFormat
 | 
						|
	ErrMsg     string
 | 
						|
}
 | 
						|
 | 
						|
type GetOAuthStatusResponse struct {
 | 
						|
	IsOauth  bool
 | 
						|
	Status   common.OAuthStatus
 | 
						|
	OAuthURL string
 | 
						|
}
 | 
						|
 | 
						|
type AgentPluginOAuthStatus struct {
 | 
						|
	PluginID      int64
 | 
						|
	PluginName    string
 | 
						|
	PluginIconURL string
 | 
						|
	Status        common.OAuthStatus
 | 
						|
}
 | 
						|
 | 
						|
type CopyPluginRequest struct {
 | 
						|
	UserID    int64
 | 
						|
	PluginID  int64
 | 
						|
	CopyScene model.CopyScene
 | 
						|
 | 
						|
	TargetAPPID *int64
 | 
						|
}
 | 
						|
 | 
						|
type CopyPluginResponse struct {
 | 
						|
	Plugin *entity.PluginInfo
 | 
						|
	Tools  map[int64]*entity.ToolInfo // old tool id -> new tool
 | 
						|
}
 | 
						|
 | 
						|
type MoveAPPPluginToLibRequest struct {
 | 
						|
	PluginID int64
 | 
						|
}
 | 
						|
 | 
						|
type GetAccessTokenRequest struct {
 | 
						|
	UserID    string
 | 
						|
	PluginID  *int64
 | 
						|
	Mode      model.AuthzSubType
 | 
						|
	OAuthInfo *entity.OAuthInfo
 | 
						|
}
 |