235 lines
7.4 KiB
Go
235 lines
7.4 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"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/mod/semver"
|
|
|
|
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/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) GetPluginNextVersion(ctx context.Context, pluginID int64) (version string, err error) {
|
|
const defaultVersion = "v1.0.0"
|
|
|
|
pl, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, pluginID)
|
|
if err != nil {
|
|
return "", errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", pluginID)
|
|
}
|
|
if !exist {
|
|
return defaultVersion, nil
|
|
}
|
|
|
|
parts := strings.Split(pl.GetVersion(), ".") // Remove the 'v' and split
|
|
if len(parts) < 3 {
|
|
logs.CtxWarnf(ctx, "invalid version format '%s'", pl.GetVersion())
|
|
return defaultVersion, nil
|
|
}
|
|
|
|
patch, err := strconv.ParseInt(parts[2], 10, 64)
|
|
if err != nil {
|
|
logs.CtxWarnf(ctx, "invalid version format '%s'", pl.GetVersion())
|
|
return defaultVersion, nil
|
|
}
|
|
|
|
parts[2] = strconv.FormatInt(patch+1, 10)
|
|
nextVersion := strings.Join(parts, ".")
|
|
|
|
return nextVersion, nil
|
|
}
|
|
|
|
func (p *pluginServiceImpl) PublishPlugin(ctx context.Context, req *PublishPluginRequest) (err error) {
|
|
draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, req.PluginID)
|
|
if err != nil {
|
|
return errorx.Wrapf(err, "GetDraftPlugin failed, pluginID=%d", req.PluginID)
|
|
}
|
|
if !exist {
|
|
return errorx.New(errno.ErrPluginRecordNotFound)
|
|
}
|
|
|
|
err = p.checkToolsDebugStatus(ctx, req.PluginID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
onlinePlugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, req.PluginID)
|
|
if err != nil {
|
|
return errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", req.PluginID)
|
|
}
|
|
if exist && onlinePlugin.Version != nil {
|
|
if semver.Compare(req.Version, *onlinePlugin.Version) != 1 {
|
|
return errorx.New(errno.ErrPluginInvalidParamCode,
|
|
errorx.KVf(errno.PluginMsgKey, "version must be greater than the online version '%s' and format like 'v1.0.0'",
|
|
*onlinePlugin.Version))
|
|
}
|
|
}
|
|
|
|
draftPlugin.Version = &req.Version
|
|
draftPlugin.VersionDesc = &req.VersionDesc
|
|
|
|
err = p.pluginRepo.PublishPlugin(ctx, draftPlugin)
|
|
if err != nil {
|
|
return errorx.Wrapf(err, "PublishPlugin failed, pluginID=%d", req.PluginID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *pluginServiceImpl) PublishAPPPlugins(ctx context.Context, req *PublishAPPPluginsRequest) (resp *PublishAPPPluginsResponse, err error) {
|
|
resp = &PublishAPPPluginsResponse{}
|
|
|
|
draftPlugins, err := p.pluginRepo.GetAPPAllDraftPlugins(ctx, req.APPID)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetAPPAllDraftPlugins failed, appID=%d", req.APPID)
|
|
}
|
|
|
|
failedPluginIDs, err := p.checkCanPublishAPPPlugins(ctx, req.Version, draftPlugins)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "checkCanPublishAPPPlugins failed, appID=%d, appVerion=%s", req.APPID, req.Version)
|
|
}
|
|
|
|
for _, draftPlugin := range draftPlugins {
|
|
draftPlugin.Version = &req.Version
|
|
draftPlugin.VersionDesc = ptr.Of(fmt.Sprintf("publish %s", req.Version))
|
|
resp.AllDraftPlugins = append(resp.AllDraftPlugins, draftPlugin.PluginInfo)
|
|
}
|
|
|
|
if len(failedPluginIDs) > 0 {
|
|
draftPluginMap := slices.ToMap(draftPlugins, func(plugin *entity.PluginInfo) (int64, *entity.PluginInfo) {
|
|
return plugin.ID, plugin
|
|
})
|
|
|
|
failedPlugins := make([]*entity.PluginInfo, 0, len(failedPluginIDs))
|
|
for _, failedPluginID := range failedPluginIDs {
|
|
failedPlugins = append(failedPlugins, draftPluginMap[failedPluginID])
|
|
}
|
|
for _, failedPlugin := range failedPlugins {
|
|
resp.FailedPlugins = append(resp.FailedPlugins, failedPlugin.PluginInfo)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
err = p.pluginRepo.PublishPlugins(ctx, draftPlugins)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "PublishPlugins failed, appID=%d", req.APPID)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *pluginServiceImpl) checkCanPublishAPPPlugins(ctx context.Context, version string, draftPlugins []*entity.PluginInfo) (failedPluginIDs []int64, err error) {
|
|
failedPluginIDs = make([]int64, 0, len(draftPlugins))
|
|
|
|
draftPluginIDs := slices.Transform(draftPlugins, func(plugin *entity.PluginInfo) int64 {
|
|
return plugin.ID
|
|
})
|
|
|
|
// 1. check version
|
|
onlinePlugins, err := p.pluginRepo.MGetOnlinePlugins(ctx, draftPluginIDs,
|
|
repository.WithPluginID(),
|
|
repository.WithPluginVersion())
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "MGetOnlinePlugins failed, pluginIDs=%v", draftPluginIDs)
|
|
}
|
|
|
|
if len(onlinePlugins) > 0 {
|
|
for _, onlinePlugin := range onlinePlugins {
|
|
if onlinePlugin.Version == nil {
|
|
continue
|
|
}
|
|
if semver.Compare(version, *onlinePlugin.Version) != 1 {
|
|
failedPluginIDs = append(failedPluginIDs, onlinePlugin.ID)
|
|
}
|
|
}
|
|
if len(failedPluginIDs) > 0 {
|
|
logs.CtxErrorf(ctx, "invalid version of plugins '%v'", failedPluginIDs)
|
|
return failedPluginIDs, nil
|
|
}
|
|
}
|
|
|
|
// 2. check debug status
|
|
for _, draftPlugin := range draftPlugins {
|
|
err = p.checkToolsDebugStatus(ctx, draftPlugin.ID)
|
|
if err != nil {
|
|
failedPluginIDs = append(failedPluginIDs, draftPlugin.ID)
|
|
logs.CtxErrorf(ctx, "checkToolsDebugStatus failed, pluginID=%d, err=%s", draftPlugin.ID, err)
|
|
}
|
|
}
|
|
|
|
if len(failedPluginIDs) > 0 {
|
|
return failedPluginIDs, nil
|
|
}
|
|
|
|
return failedPluginIDs, nil
|
|
}
|
|
|
|
func (p *pluginServiceImpl) checkToolsDebugStatus(ctx context.Context, pluginID int64) (err error) {
|
|
res, err := p.toolRepo.GetPluginAllDraftTools(ctx, pluginID,
|
|
repository.WithToolID(),
|
|
repository.WithToolDebugStatus(),
|
|
repository.WithToolActivatedStatus(),
|
|
)
|
|
if err != nil {
|
|
return errorx.Wrapf(err, "GetPluginAllDraftTools failed, pluginID=%d", pluginID)
|
|
}
|
|
|
|
if len(res) == 0 {
|
|
return errorx.New(errno.ErrPluginToolsCheckFailed, errorx.KVf(errno.PluginMsgKey,
|
|
"at least one activated tool is required in plugin"))
|
|
}
|
|
|
|
activatedTools := make([]*entity.ToolInfo, 0, len(res))
|
|
for _, tool := range res {
|
|
if tool.GetActivatedStatus() == model.DeactivateTool {
|
|
continue
|
|
}
|
|
activatedTools = append(activatedTools, tool)
|
|
}
|
|
|
|
if len(activatedTools) == 0 {
|
|
return errorx.New(errno.ErrPluginToolsCheckFailed, errorx.KVf(errno.PluginMsgKey,
|
|
"at least one activated tool is required in plugin"))
|
|
}
|
|
|
|
for _, tool := range activatedTools {
|
|
if tool.GetDebugStatus() != common.APIDebugStatus_DebugWaiting {
|
|
continue
|
|
}
|
|
return errorx.New(errno.ErrPluginToolsCheckFailed, errorx.KVf(errno.PluginMsgKey,
|
|
"tools in plugin have not debugged yet"))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *pluginServiceImpl) CheckPluginToolsDebugStatus(ctx context.Context, pluginID int64) (err error) {
|
|
return p.checkToolsDebugStatus(ctx, pluginID)
|
|
}
|