mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
555 lines
15 KiB
Go
555 lines
15 KiB
Go
package release
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/APIParkLab/APIPark/service/api"
|
|
"github.com/APIParkLab/APIPark/service/universally/commit"
|
|
"github.com/APIParkLab/APIPark/service/upstream"
|
|
"github.com/APIParkLab/APIPark/stores/release"
|
|
"github.com/eolinker/go-common/auto"
|
|
"github.com/eolinker/go-common/autowire"
|
|
"github.com/eolinker/go-common/utils"
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var (
|
|
_ IReleaseService = (*imlReleaseService)(nil)
|
|
_ auto.CompleteService = (*imlReleaseService)(nil)
|
|
_ autowire.Complete = (*imlReleaseService)(nil)
|
|
)
|
|
|
|
type imlReleaseService struct {
|
|
releaseStore release.IReleaseStore `autowired:""`
|
|
commitStore release.IReleaseCommitStore `autowired:""`
|
|
releaseRuntime release.IReleaseRuntime `autowired:""`
|
|
}
|
|
|
|
func (s *imlReleaseService) UpdateRelease(ctx context.Context, id string, update *Update) error {
|
|
info, err := s.releaseStore.GetByUUID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if update.Version != nil {
|
|
info.Name = *update.Version
|
|
}
|
|
if update.Remark != nil {
|
|
info.Remark = *update.Remark
|
|
}
|
|
|
|
_, err = s.releaseStore.Update(ctx, info)
|
|
return err
|
|
}
|
|
|
|
func (s *imlReleaseService) GetRunningList(ctx context.Context, serviceId ...string) ([]*Release, error) {
|
|
w := make(map[string]interface{})
|
|
if len(serviceId) > 0 {
|
|
w["service"] = serviceId
|
|
}
|
|
list, err := s.releaseRuntime.List(ctx, w)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(list) == 0 {
|
|
return nil, nil
|
|
}
|
|
commitIds := utils.SliceToSlice(list, func(o *release.Runtime) string {
|
|
return o.Release
|
|
})
|
|
commits, err := s.releaseStore.List(ctx, map[string]interface{}{
|
|
"uuid": commitIds,
|
|
})
|
|
return utils.SliceToSlice(commits, FromEntity), err
|
|
}
|
|
|
|
func (s *imlReleaseService) GetRunningApiDocCommits(ctx context.Context, serviceIds ...string) ([]string, error) {
|
|
w := make(map[string]interface{})
|
|
if len(serviceIds) > 0 {
|
|
w["service"] = serviceIds
|
|
}
|
|
runnings, err := s.releaseRuntime.List(ctx, w)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
apiDocCommits := make([]string, 0, len(runnings))
|
|
for _, running := range runnings {
|
|
commits, err := s.getCommitByType(ctx, running.Release, CommitApiDocument, running.Service)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(commits) == 0 {
|
|
continue
|
|
}
|
|
apiDocCommits = append(apiDocCommits, commits[0].Commit)
|
|
}
|
|
return apiDocCommits, nil
|
|
}
|
|
|
|
func (s *imlReleaseService) Completeness(partitions []string, apis []string, apiRequestCommits []*commit.Commit[api.Request], proxyCommits []*commit.Commit[api.Proxy], upstreamCommits []*commit.Commit[upstream.Config]) bool {
|
|
|
|
requests := utils.SliceToMap(apiRequestCommits, func(o *commit.Commit[api.Request]) string {
|
|
return o.Target
|
|
})
|
|
proxys := utils.SliceToMap(proxyCommits, func(o *commit.Commit[api.Proxy]) string {
|
|
return o.Target
|
|
})
|
|
|
|
for _, aid := range apis {
|
|
_, has := requests[aid]
|
|
if !has {
|
|
return false
|
|
}
|
|
_, has = proxys[aid]
|
|
if !has {
|
|
return false
|
|
}
|
|
|
|
}
|
|
upstreamMap := make(map[string]map[string]struct{})
|
|
for _, upstreamCommit := range upstreamCommits {
|
|
if _, has := upstreamMap[upstreamCommit.Target]; !has {
|
|
upstreamMap[upstreamCommit.Target] = make(map[string]struct{})
|
|
}
|
|
upstreamMap[upstreamCommit.Target][upstreamCommit.Key] = struct{}{}
|
|
}
|
|
|
|
for _, partition := range partitions {
|
|
for _, u := range upstreamMap {
|
|
if _, has := u[partition]; !has {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (s *imlReleaseService) GetCommits(ctx context.Context, id string) ([]*ProjectCommits, error) {
|
|
list, err := s.commitStore.List(ctx, map[string]interface{}{
|
|
"release": id,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return utils.SliceToSlice(list, func(o *release.Commit) *ProjectCommits {
|
|
return &ProjectCommits{
|
|
Release: o.Release,
|
|
Target: o.Target,
|
|
Key: o.Key,
|
|
Type: o.Type,
|
|
Commit: o.Commit,
|
|
}
|
|
}), nil
|
|
}
|
|
|
|
func (s *imlReleaseService) OnComplete() {
|
|
auto.RegisterService("release", s)
|
|
}
|
|
|
|
func (s *imlReleaseService) GetLabels(ctx context.Context, ids ...string) map[string]string {
|
|
if len(ids) == 0 {
|
|
return nil
|
|
}
|
|
if len(ids) == 1 {
|
|
o, err := s.releaseStore.GetByUUID(ctx, ids[0])
|
|
if err != nil || o == nil {
|
|
return nil
|
|
}
|
|
return map[string]string{
|
|
o.UUID: o.Name,
|
|
}
|
|
}
|
|
list, err := s.releaseStore.ListQuery(ctx, "`uuid` in ?", []interface{}{ids}, "id")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return utils.SliceToMapO(list, func(o *release.Release) (string, string) { return o.UUID, o.Name })
|
|
}
|
|
|
|
func (s *imlReleaseService) GetApiProxyCommit(ctx context.Context, id string, apiUUID string) (string, error) {
|
|
commits, err := s.getCommitByType(ctx, id, CommitApiProxy, apiUUID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(commits) == 0 {
|
|
return "", errors.New("not found")
|
|
}
|
|
|
|
return commits[0].Commit, nil
|
|
}
|
|
|
|
func (s *imlReleaseService) getCommitByType(ctx context.Context, releaseId, t CommitType, target string) ([]*release.Commit, error) {
|
|
where := "`release` = ? and `type` = ? and `target` = ?"
|
|
args := []interface{}{releaseId, t, target}
|
|
return s.commitStore.ListQuery(ctx, where, args, "")
|
|
|
|
}
|
|
|
|
//func (s *imlReleaseService) GetApiDocCommit(ctx context.Context, id string, apiUUID string) (string, error) {
|
|
// commits, err := s.getCommitByType(ctx, id, CommitApiDocument, apiUUID, CommitApiDocument)
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
// if len(commits) == 0 {
|
|
// return "", errors.New("not found")
|
|
// }
|
|
//
|
|
// return commits[0].Commit, nil
|
|
//}
|
|
|
|
//func (s *imlReleaseService) GetRunningApiDocCommit(ctx context.Context, service string, apiUUID string) (string, error) {
|
|
// running, err := s.releaseRuntime.First(ctx, map[string]interface{}{
|
|
// "service": service,
|
|
// })
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
// return s.GetApiDocCommit(ctx, running.Release, apiUUID)
|
|
//
|
|
//}
|
|
|
|
func (s *imlReleaseService) GetRunningApiProxyCommit(ctx context.Context, service string, apiUUID string) (string, error) {
|
|
running, err := s.releaseRuntime.First(ctx, map[string]interface{}{
|
|
"service": service,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return s.GetApiProxyCommit(ctx, running.Release, apiUUID)
|
|
}
|
|
|
|
//
|
|
//func (s *imlReleaseService) DiffApis(ctx context.Context, baseApis []*Api, targetApis []*Api) []*APiDiff {
|
|
// result := make([]*APiDiff, 0, len(targetApis)+len(baseApis))
|
|
// baseApiMap := utils.SliceToMap(baseApis, func(v *Api) string {
|
|
// return v.Api
|
|
// })
|
|
// for _, targetApi := range targetApis {
|
|
// if baseApi, ok := baseApiMap[targetApi.Api]; ok {
|
|
// if baseApi.ProxyCommit != targetApi.ProxyCommit || baseApi.DocCommit != targetApi.DocCommit {
|
|
// result = append(result, &APiDiff{
|
|
// Api: targetApi.Api,
|
|
// Change: project_diff.ChangeTypeUpdate,
|
|
// })
|
|
// } else {
|
|
// result = append(result, &APiDiff{
|
|
// Api: targetApi.Api,
|
|
// Change: project_diff.ChangeTypeNone,
|
|
// })
|
|
// }
|
|
// delete(baseApiMap, targetApi.Api)
|
|
// } else {
|
|
// result = append(result, &APiDiff{
|
|
// Api: targetApi.Api,
|
|
// Change: project_diff.ChangeTypeNew,
|
|
// })
|
|
// }
|
|
// }
|
|
// for _, baseApi := range baseApiMap {
|
|
// result = append(result, &APiDiff{
|
|
// Api: baseApi.Api,
|
|
// Change: project_diff.ChangeTypeDelete,
|
|
// })
|
|
// }
|
|
// return result
|
|
//}
|
|
//
|
|
//func (s *imlReleaseService) DiffUpstreams(ctx context.Context, baseUpstreams []*UpstreamCommit, targetUpstreams []*UpstreamCommit) []*UpstreamDiff {
|
|
// Upstreams := make([]*UpstreamDiff, 0, len(targetUpstreams)+len(baseUpstreams))
|
|
// baseUpstreamMap := utils.SliceToMap(baseUpstreams, func(v *UpstreamCommit) string {
|
|
// return fmt.Sprintf("%s-%s", v.UpstreamCommit, v.Cluster)
|
|
// })
|
|
// for _, targetUpstream := range targetUpstreams {
|
|
// key := fmt.Sprintf("%s-%s", targetUpstream.UpstreamCommit, targetUpstream.Cluster)
|
|
// if baseUpstream, ok := baseUpstreamMap[key]; ok {
|
|
// if baseUpstream.Commit != targetUpstream.Commit {
|
|
// Upstreams = append(Upstreams, &UpstreamDiff{
|
|
// UpstreamCommit: targetUpstream.UpstreamCommit,
|
|
// Cluster: targetUpstream.Cluster,
|
|
// Commit: targetUpstream.Commit,
|
|
// Change: project_diff.ChangeTypeUpdate,
|
|
// })
|
|
// } else {
|
|
// Upstreams = append(Upstreams, &UpstreamDiff{
|
|
// UpstreamCommit: targetUpstream.UpstreamCommit,
|
|
// Cluster: targetUpstream.Cluster,
|
|
// Commit: targetUpstream.Commit,
|
|
// Change: project_diff.ChangeTypeNone,
|
|
// })
|
|
// }
|
|
// delete(baseUpstreamMap, targetUpstream.UpstreamCommit)
|
|
// } else {
|
|
// Upstreams = append(Upstreams, &UpstreamDiff{
|
|
// UpstreamCommit: targetUpstream.UpstreamCommit,
|
|
// Cluster: targetUpstream.Cluster,
|
|
// Commit: targetUpstream.Commit,
|
|
// Change: project_diff.ChangeTypeNew,
|
|
// })
|
|
// }
|
|
// }
|
|
// for _, baseUpstream := range baseUpstreamMap {
|
|
// Upstreams = append(Upstreams, &UpstreamDiff{
|
|
// UpstreamCommit: baseUpstream.UpstreamCommit,
|
|
// Cluster: baseUpstream.Cluster,
|
|
// Commit: baseUpstream.Commit,
|
|
// Change: project_diff.ChangeTypeDelete,
|
|
// })
|
|
// }
|
|
// return Upstreams
|
|
//}
|
|
|
|
func (s *imlReleaseService) SetRunning(ctx context.Context, service string, id string) error {
|
|
_, err := s.releaseRuntime.DeleteWhere(ctx, map[string]interface{}{"service": service})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
operator := utils.UserId(ctx)
|
|
return s.releaseRuntime.Save(ctx, &release.Runtime{
|
|
Id: 0,
|
|
Service: service,
|
|
Release: id,
|
|
UpdateTime: time.Now(),
|
|
Operator: operator,
|
|
})
|
|
|
|
}
|
|
|
|
func (s *imlReleaseService) CreateRelease(ctx context.Context, service, version, remark string, apiRequestCommit, apisProxyCommits map[string]string, apiDocCommits, serviceDocCommits string, upstreams map[string]map[string]string, strategies map[string]string) (*Release, error) {
|
|
operator := utils.UserId(ctx)
|
|
releaseId := uuid.NewString()
|
|
commits := make([]*release.Commit, 0, len(apisProxyCommits)+len(upstreams)+2)
|
|
for apiId, commitUUID := range apiRequestCommit {
|
|
commits = append(commits, &release.Commit{
|
|
Type: CommitApiRequest,
|
|
Target: apiId,
|
|
Release: releaseId,
|
|
Key: CommitApiRequest,
|
|
Commit: commitUUID,
|
|
})
|
|
}
|
|
for aid, commitUUID := range apisProxyCommits {
|
|
commits = append(commits, &release.Commit{
|
|
Type: CommitApiProxy,
|
|
Target: aid,
|
|
Release: releaseId,
|
|
Key: CommitApiProxy,
|
|
Commit: commitUUID,
|
|
})
|
|
}
|
|
|
|
for upId, upstreamsByPartition := range upstreams {
|
|
for partition, commitUUID := range upstreamsByPartition {
|
|
commits = append(commits, &release.Commit{
|
|
Type: CommitUpstream,
|
|
Target: upId,
|
|
Release: releaseId,
|
|
Key: partition,
|
|
Commit: commitUUID,
|
|
})
|
|
}
|
|
}
|
|
if apiDocCommits != "" {
|
|
commits = append(commits, &release.Commit{
|
|
Type: CommitApiDocument,
|
|
Target: service,
|
|
Release: releaseId,
|
|
Key: CommitApiDocument,
|
|
Commit: apiDocCommits,
|
|
})
|
|
}
|
|
|
|
if serviceDocCommits != "" {
|
|
commits = append(commits, &release.Commit{
|
|
Type: CommitServiceDoc,
|
|
Target: service,
|
|
Release: releaseId,
|
|
Key: CommitServiceDoc,
|
|
Commit: serviceDocCommits,
|
|
})
|
|
}
|
|
for key, value := range strategies {
|
|
commits = append(commits, &release.Commit{
|
|
Type: CommitStrategy,
|
|
Target: key,
|
|
Release: releaseId,
|
|
Key: CommitStrategy,
|
|
Commit: value,
|
|
})
|
|
}
|
|
|
|
ev := &release.Release{
|
|
Id: 0,
|
|
UUID: releaseId,
|
|
Name: version,
|
|
Service: service,
|
|
Remark: remark,
|
|
Creator: operator,
|
|
CreateAt: time.Now(),
|
|
}
|
|
err := s.releaseStore.Transaction(ctx, func(ctx context.Context) error {
|
|
ok, e := s.CheckNewVersion(ctx, service, version)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
if !ok {
|
|
return errors.New("version already exists")
|
|
}
|
|
|
|
err := s.releaseStore.Insert(ctx, ev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.commitStore.Insert(ctx, commits...)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return FromEntity(ev), nil
|
|
}
|
|
|
|
func (s *imlReleaseService) CheckNewVersion(ctx context.Context, service string, version string) (bool, error) {
|
|
v, err := s.releaseStore.First(ctx, map[string]interface{}{
|
|
"service": service,
|
|
"name": version,
|
|
})
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return false, err
|
|
}
|
|
if errors.Is(err, gorm.ErrRecordNotFound) || v == nil {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (s *imlReleaseService) GetRelease(ctx context.Context, id string) (*Release, error) {
|
|
r, err := s.releaseStore.GetByUUID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return FromEntity(r), nil
|
|
}
|
|
|
|
//
|
|
//func (s *imlReleaseService) Diff(ctx context.Context, baseReleaseId string, targetReleaseId string) (*Diff, error) {
|
|
// if baseReleaseId != "" || targetReleaseId != "" {
|
|
// return nil, errors.New("not support")
|
|
// }
|
|
// baseApis, baseUpstreams, err := s.GetReleaseInfos(ctx, baseReleaseId)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// targetApis, targetUpstreams, err := s.GetReleaseInfos(ctx, targetReleaseId)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
//
|
|
// df := new(Diff)
|
|
// df.Routers = s.DiffApis(ctx, baseApis, targetApis)
|
|
// df.Upstreams = s.DiffUpstreams(ctx, baseUpstreams, targetUpstreams)
|
|
// return df, nil
|
|
//}
|
|
|
|
func (s *imlReleaseService) DeleteRelease(ctx context.Context, id string) error {
|
|
//todo 判断版本是否有使用中的未完结流程
|
|
|
|
return s.releaseStore.Transaction(ctx, func(ctx context.Context) error {
|
|
first, err := s.releaseRuntime.First(ctx, map[string]interface{}{
|
|
"release": id,
|
|
})
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
if err == nil && first != nil {
|
|
return errors.New("release is in use")
|
|
}
|
|
err = s.releaseStore.DeleteUUID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = s.commitStore.DeleteWhere(ctx, map[string]interface{}{
|
|
"release": id,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
|
|
}
|
|
|
|
func (s *imlReleaseService) List(ctx context.Context, service string) ([]*Release, error) {
|
|
list, err := s.releaseStore.List(ctx, map[string]interface{}{"service": service}, "create_at desc")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return utils.SliceToSlice(list, FromEntity), nil
|
|
}
|
|
|
|
func (s *imlReleaseService) GetReleaseInfos(ctx context.Context, id string) ([]*APICommit, []*APICommit, *APICommit, []*UpstreamCommit, *ServiceCommit, error) {
|
|
commits, err := s.commitStore.List(ctx, map[string]interface{}{
|
|
"release": id,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, nil, nil, nil, err
|
|
}
|
|
apiRequestCommits := make([]*APICommit, 0, len(commits))
|
|
apiProxyCommits := make([]*APICommit, 0, len(commits))
|
|
var apiDocCommit *APICommit
|
|
upstreamCommits := make([]*UpstreamCommit, 0, len(commits))
|
|
var serviceDocCommit *ServiceCommit
|
|
for _, v := range commits {
|
|
switch v.Type {
|
|
case CommitApiRequest:
|
|
apiRequestCommits = append(apiRequestCommits, &APICommit{
|
|
Release: v.Release,
|
|
API: v.Target,
|
|
Commit: v.Commit,
|
|
})
|
|
case CommitApiProxy:
|
|
apiProxyCommits = append(apiProxyCommits, &APICommit{
|
|
Release: v.Release,
|
|
API: v.Target,
|
|
Commit: v.Commit,
|
|
})
|
|
|
|
case CommitApiDocument:
|
|
apiDocCommit = &APICommit{
|
|
Release: v.Release,
|
|
API: v.Target,
|
|
Commit: v.Commit,
|
|
}
|
|
|
|
case CommitUpstream:
|
|
upstreamCommits = append(upstreamCommits, &UpstreamCommit{
|
|
Release: v.Release,
|
|
Upstream: v.Target,
|
|
Partition: v.Key,
|
|
Commit: v.Commit,
|
|
})
|
|
case CommitServiceDoc:
|
|
serviceDocCommit = &ServiceCommit{
|
|
Release: v.Release,
|
|
Service: v.Target,
|
|
Commit: v.Commit,
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return apiRequestCommits, apiProxyCommits, apiDocCommit, upstreamCommits, serviceDocCommit, nil
|
|
}
|
|
|
|
func (s *imlReleaseService) GetRunning(ctx context.Context, service string) (*Release, error) {
|
|
running, err := s.releaseRuntime.First(ctx, map[string]interface{}{
|
|
"service": service,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.GetRelease(ctx, running.Release)
|
|
}
|