coze-studio/backend/domain/plugin/service/plugin_online.go

383 lines
12 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"
"sort"
model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
searchModel "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/search"
pluginCommon "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop/common"
resCommon "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
crosssearch "github.com/coze-dev/coze-studio/backend/crossdomain/contract/search"
pluginConf "github.com/coze-dev/coze-studio/backend/domain/plugin/conf"
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
"github.com/coze-dev/coze-studio/backend/domain/plugin/repository"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
func (p *pluginServiceImpl) GetOnlinePlugin(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, err error) {
pl, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pluginID)
if err != nil {
return nil, errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", pluginID)
}
if !exist {
return nil, errorx.New(errno.ErrPluginRecordNotFound)
}
return pl, nil
}
func (p *pluginServiceImpl) MGetOnlinePlugins(ctx context.Context, pluginIDs []int64) (plugins []*entity.PluginInfo, err error) {
plugins, err = p.pluginRepo.MGetOnlinePlugins(ctx, pluginIDs)
if err != nil {
return nil, errorx.Wrapf(err, "MGetOnlinePlugins failed, pluginIDs=%v", pluginIDs)
}
res := make([]*model.PluginInfo, 0, len(plugins))
for _, pl := range plugins {
res = append(res, pl.PluginInfo)
}
return plugins, nil
}
func (p *pluginServiceImpl) GetOnlineTool(ctx context.Context, toolID int64) (tool *entity.ToolInfo, err error) {
tool, exist, err := p.toolRepo.GetOnlineTool(ctx, toolID)
if err != nil {
return nil, errorx.Wrapf(err, "GetOnlineTool failed, toolID=%d", toolID)
}
if !exist {
return nil, errorx.New(errno.ErrPluginRecordNotFound)
}
return tool, nil
}
func (p *pluginServiceImpl) MGetOnlineTools(ctx context.Context, toolIDs []int64) (tools []*entity.ToolInfo, err error) {
tools, err = p.toolRepo.MGetOnlineTools(ctx, toolIDs)
if err != nil {
return nil, errorx.Wrapf(err, "MGetOnlineTools failed, toolIDs=%v", toolIDs)
}
return tools, nil
}
func (p *pluginServiceImpl) MGetVersionTools(ctx context.Context, versionTools []entity.VersionTool) (tools []*entity.ToolInfo, err error) {
tools, err = p.toolRepo.MGetVersionTools(ctx, versionTools)
if err != nil {
return nil, errorx.Wrapf(err, "MGetVersionTools failed, versionTools=%v", versionTools)
}
return tools, nil
}
func (p *pluginServiceImpl) ListPluginProducts(ctx context.Context, req *ListPluginProductsRequest) (resp *ListPluginProductsResponse, err error) {
plugins := slices.Transform(pluginConf.GetAllPluginProducts(), func(p *pluginConf.PluginInfo) *entity.PluginInfo {
return entity.NewPluginInfo(p.Info)
})
sort.Slice(plugins, func(i, j int) bool {
return plugins[i].GetRefProductID() < plugins[j].GetRefProductID()
})
return &ListPluginProductsResponse{
Plugins: plugins,
Total: int64(len(plugins)),
}, nil
}
func (p *pluginServiceImpl) GetPluginProductAllTools(ctx context.Context, pluginID int64) (tools []*entity.ToolInfo, err error) {
res, err := p.toolRepo.GetPluginAllOnlineTools(ctx, pluginID)
if err != nil {
return nil, errorx.Wrapf(err, "GetPluginAllOnlineTools failed, pluginID=%d", pluginID)
}
return res, nil
}
func (p *pluginServiceImpl) DeleteAPPAllPlugins(ctx context.Context, appID int64) (pluginIDs []int64, err error) {
return p.pluginRepo.DeleteAPPAllPlugins(ctx, appID)
}
func (p *pluginServiceImpl) GetAPPAllPlugins(ctx context.Context, appID int64) (plugins []*entity.PluginInfo, err error) {
plugins, err = p.pluginRepo.GetAPPAllDraftPlugins(ctx, appID)
if err != nil {
return nil, errorx.Wrapf(err, "GetAPPAllDraftPlugins failed, appID=%d", appID)
}
return plugins, nil
}
func (p *pluginServiceImpl) MGetVersionPlugins(ctx context.Context, versionPlugins []entity.VersionPlugin) (plugins []*entity.PluginInfo, err error) {
plugins, err = p.pluginRepo.MGetVersionPlugins(ctx, versionPlugins)
if err != nil {
return nil, errorx.Wrapf(err, "MGetVersionPlugins failed, versionPlugins=%v", versionPlugins)
}
return plugins, nil
}
func (p *pluginServiceImpl) ListCustomOnlinePlugins(ctx context.Context, spaceID int64, pageInfo entity.PageInfo) (plugins []*entity.PluginInfo, total int64, err error) {
if pageInfo.Name == nil || *pageInfo.Name == "" {
plugins, total, err = p.pluginRepo.ListCustomOnlinePlugins(ctx, spaceID, pageInfo)
if err != nil {
return nil, 0, errorx.Wrapf(err, "ListCustomOnlinePlugins failed, spaceID=%d", spaceID)
}
return plugins, total, nil
}
res, err := crosssearch.DefaultSVC().SearchResources(ctx, &searchModel.SearchResourcesRequest{
SpaceID: spaceID,
Name: *pageInfo.Name,
OrderAsc: false,
ResTypeFilter: []resCommon.ResType{
resCommon.ResType_Plugin,
},
OrderFiledName: func() string {
if pageInfo.SortBy == nil || *pageInfo.SortBy != entity.SortByCreatedAt {
return searchModel.FieldOfUpdateTime
}
return searchModel.FieldOfCreateTime
}(),
Page: ptr.Of(int32(pageInfo.Page)),
Limit: int32(pageInfo.Size),
})
if err != nil {
return nil, 0, errorx.Wrapf(err, "SearchResources failed, spaceID=%d", spaceID)
}
plugins = make([]*entity.PluginInfo, 0, len(res.Data))
for _, pl := range res.Data {
draftPlugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pl.ResID)
if err != nil {
return nil, 0, errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", pl.ResID)
}
if !exist {
logs.CtxWarnf(ctx, "online plugin not exist, pluginID=%d", pl.ResID)
continue
}
plugins = append(plugins, draftPlugin)
}
if res.TotalHits != nil {
total = *res.TotalHits
}
return plugins, total, nil
}
func (p *pluginServiceImpl) MGetPluginLatestVersion(ctx context.Context, pluginIDs []int64) (resp *MGetPluginLatestVersionResponse, err error) {
plugins, err := p.pluginRepo.MGetOnlinePlugins(ctx, pluginIDs,
repository.WithPluginID(),
repository.WithPluginVersion())
if err != nil {
return nil, errorx.Wrapf(err, "MGetOnlinePlugins failed, pluginIDs=%v", pluginIDs)
}
versions := make(map[int64]string, len(plugins))
for _, pl := range plugins {
versions[pl.ID] = pl.GetVersion()
}
resp = &MGetPluginLatestVersionResponse{
Versions: versions,
}
return resp, nil
}
func (p *pluginServiceImpl) CopyPlugin(ctx context.Context, req *CopyPluginRequest) (resp *CopyPluginResponse, err error) {
err = p.checkCanCopyPlugin(ctx, req.PluginID, req.CopyScene)
if err != nil {
return nil, err
}
plugin, tools, err := p.getCopySourcePluginAndTools(ctx, req.PluginID, req.CopyScene)
if err != nil {
return nil, err
}
p.changePluginAndToolsInfoForCopy(req, plugin, tools)
toolMap := make(map[int64]*entity.ToolInfo, len(tools))
for _, tool := range tools {
toolMap[tool.ID] = tool
}
plugin, tools, err = p.pluginRepo.CopyPlugin(ctx, &repository.CopyPluginRequest{
Plugin: plugin,
Tools: tools,
})
if err != nil {
return nil, errorx.Wrapf(err, "CopyPlugin failed, pluginID=%d", req.PluginID)
}
resp = &CopyPluginResponse{
Plugin: plugin,
Tools: toolMap,
}
return resp, nil
}
func (p *pluginServiceImpl) changePluginAndToolsInfoForCopy(req *CopyPluginRequest, plugin *entity.PluginInfo, tools []*entity.ToolInfo) {
plugin.Version = nil
plugin.VersionDesc = nil
plugin.DeveloperID = req.UserID
if req.CopyScene != model.CopySceneOfAPPDuplicate {
plugin.SetName(fmt.Sprintf("%s_copy", plugin.GetName()))
}
if req.CopyScene == model.CopySceneOfToLibrary {
const (
defaultVersion = "v0.0.1"
defaultVersionDesc = "copy to library"
)
plugin.APPID = nil
plugin.Version = ptr.Of(defaultVersion)
plugin.VersionDesc = ptr.Of(defaultVersionDesc)
for _, tool := range tools {
tool.Version = ptr.Of(defaultVersion)
}
}
if req.CopyScene == model.CopySceneOfToAPP {
plugin.APPID = req.TargetAPPID
for _, tool := range tools {
tool.DebugStatus = ptr.Of(pluginCommon.APIDebugStatus_DebugPassed)
}
}
if req.CopyScene == model.CopySceneOfAPPDuplicate {
plugin.APPID = req.TargetAPPID
}
}
func (p *pluginServiceImpl) checkCanCopyPlugin(ctx context.Context, pluginID int64, scene model.CopyScene) (err error) {
switch scene {
case model.CopySceneOfToAPP, model.CopySceneOfDuplicate, model.CopySceneOfAPPDuplicate:
return nil
case model.CopySceneOfToLibrary:
return p.checkToolsDebugStatus(ctx, pluginID)
default:
return fmt.Errorf("unsupported copy scene '%s'", scene)
}
}
func (p *pluginServiceImpl) getCopySourcePluginAndTools(ctx context.Context, pluginID int64, scene model.CopyScene) (plugin *entity.PluginInfo, tools []*entity.ToolInfo, err error) {
switch scene {
case model.CopySceneOfToAPP:
return p.getOnlinePluginAndTools(ctx, pluginID)
case model.CopySceneOfToLibrary, model.CopySceneOfDuplicate, model.CopySceneOfAPPDuplicate:
return p.getDraftPluginAndTools(ctx, pluginID)
default:
return nil, nil, fmt.Errorf("unsupported copy scene '%s'", scene)
}
}
func (p *pluginServiceImpl) getOnlinePluginAndTools(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, tools []*entity.ToolInfo, err error) {
onlinePlugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pluginID)
if err != nil {
return nil, nil, err
}
if !exist {
return nil, nil, errorx.New(errno.ErrPluginRecordNotFound)
}
onlineTools, err := p.toolRepo.GetPluginAllOnlineTools(ctx, pluginID)
if err != nil {
return nil, nil, err
}
return onlinePlugin, onlineTools, nil
}
func (p *pluginServiceImpl) getDraftPluginAndTools(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, tools []*entity.ToolInfo, err error) {
draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, pluginID)
if err != nil {
return nil, nil, err
}
if !exist {
return nil, nil, errorx.New(errno.ErrPluginRecordNotFound)
}
draftTools, err := p.toolRepo.GetPluginAllDraftTools(ctx, pluginID)
if err != nil {
return nil, nil, err
}
return draftPlugin, draftTools, nil
}
func (p *pluginServiceImpl) MoveAPPPluginToLibrary(ctx context.Context, pluginID int64) (draftPlugin *entity.PluginInfo, err error) {
draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, pluginID)
if err != nil {
return nil, err
}
if !exist {
return nil, errorx.New(errno.ErrPluginRecordNotFound)
}
err = p.checkToolsDebugStatus(ctx, pluginID)
if err != nil {
return nil, err
}
draftTools, err := p.toolRepo.GetPluginAllDraftTools(ctx, pluginID)
if err != nil {
return nil, err
}
p.changePluginAndToolsInfoForMove(draftPlugin, draftTools)
err = p.pluginRepo.MoveAPPPluginToLibrary(ctx, draftPlugin, draftTools)
if err != nil {
return nil, errorx.Wrapf(err, "MoveAPPPluginToLibrary failed, pluginID=%d", pluginID)
}
return draftPlugin, nil
}
func (p *pluginServiceImpl) changePluginAndToolsInfoForMove(plugin *entity.PluginInfo,
tools []*entity.ToolInfo) {
const (
defaultVersion = "v0.0.1"
defaultVersionDesc = "move to library"
)
plugin.Version = ptr.Of(defaultVersion)
plugin.VersionDesc = ptr.Of(defaultVersionDesc)
for _, tool := range tools {
tool.Version = ptr.Of(defaultVersion)
}
plugin.APPID = nil
}