Skip to content
Snippets Groups Projects
Commit 585b6395 authored by Valera Shaitorov's avatar Valera Shaitorov :alien:
Browse files

Добавлен Setup и Extension

parent 283bf099
No related branches found
No related tags found
No related merge requests found
package extension
import (
"git.perx.ru/perxis/perxis-go/pkg/references"
pb "git.perx.ru/perxis/perxis-go/proto/extensions"
"github.com/mitchellh/mapstructure"
)
const ActionsCollectionID = "space_actions"
type (
ActionKind = pb.Action_Kind
//ActionRequest = pb.ActionRequest
//ActionResponse = pb.ActionResponse
)
const (
ResponseDone = pb.ActionResponse_DONE
ResponseError = pb.ActionResponse_ERROR
ResponsePending = pb.ActionResponse_PENDING
ResponseInProgress = pb.ActionResponse_IN_PROGRESS
ResponseParametersRequired = pb.ActionResponse_PARAMETERS_REQUIRED
)
type Action struct {
Extension string `mapstructure:"extension,omitempty"` // Расширение
Action string `mapstructure:"action,omitempty"` // Идентификатор действия
Name string `mapstructure:"name,omitempty"` // Название действия для отображения в интерфейсе (пункт меню, кнопка).
Description string `mapstructure:"description,omitempty"` // Описание действия для отображения в интерфейсе
Icon string `mapstructure:"icon,omitempty"` // Название иконки для отображения действия в интерфейсе
Image *references.Reference `mapstructure:"image,omitempty"` // Изображение для отображения в действия в интерфейсе
Groups []string `mapstructure:"groups,omitempty"` // Группы отображения действия в интерфейсе
Kind ActionKind `mapstructure:"kind,omitempty"` // Указывает на что направлено действие
Classes []string `mapstructure:"classes,omitempty"` // Классы данных к которым применимо действие (название коллекций или специальных групп в рамках которых данное действие применимо)
Refs []*references.Reference `mapstructure:"refs,omitempty"` // Ссылки на записи используемые для выполнения действия (назначение ссылок зависит от действия и расширения)
ParamsCollection string `mapstructure:"params_collection,omitempty"`
Request *ActionRequest `mapstructure:"request,omitempty"` // Параметры запроса (используется в случае `ActionResponse.next`)
NavigationAction bool `mapstructure:"navigation_action,omitempty"` // Флаг указывающий что действие переносить пользователя в другую часть интерфейса, а не отправляет запрос на сервер
NavigationRoute string `mapstructure:"navigation_route,omitempty"`
}
func ActionToMap(action *Action) map[string]interface{} {
res := make(map[string]interface{})
mapstructure.Decode(action, &res)
res["kind"] = int64(action.Kind.Number())
return res
}
func ActionFromMap(d map[string]interface{}) (*Action, error) {
var action Action
err := mapstructure.Decode(d, &action)
return &action, err
}
func ActionFromPB(a *pb.Action) *Action {
return &Action{
Extension: a.Extension,
Action: a.Action,
Name: a.Name,
Description: a.Description,
Icon: a.Icon,
Image: references.ReferenceFromPB(a.Image),
Groups: a.Groups,
Kind: a.Kind,
Classes: a.Classes,
Refs: references.ReferenceListFromPB(a.Refs),
ParamsCollection: a.ParamsCollection,
Request: a.Request,
NavigationAction: a.NavigationAction,
NavigationRoute: a.NavigationRoute,
}
}
package extension
import (
"context"
"git.perx.ru/perxis/perxis-go/pkg/errors"
pb "git.perx.ru/perxis/perxis-go/proto/extensions"
"google.golang.org/grpc"
)
type Client struct {
client pb.ExtensionClient
}
func NewClient(conn *grpc.ClientConn) *Client {
return &Client{
client: pb.NewExtensionClient(conn),
}
}
func (c Client) GetDescriptor() *ExtensionDescriptor {
return nil
}
type results interface {
GetResults() []*RequestResult
}
func getErrors(out results, err, wrapErr error) error {
if err != nil {
return err
}
res := out.GetResults()
var errs []error
for _, r := range res {
if r.State == RequestError {
err := errors.New(r.GetError())
if msg := r.GetMsg(); msg != "" {
err = errors.WithDetail(err, msg)
}
errs = append(errs, ExtensionError(err, r.Extension))
}
}
if len(errs) > 0 {
return errors.WithErrors(wrapErr, errs...)
}
return nil
}
func (c Client) Install(ctx context.Context, in *InstallRequest) error {
out, err := c.client.Install(ctx, in)
return getErrors(out, err, ErrInstall)
}
func (c Client) Check(ctx context.Context, in *CheckRequest) error {
out, err := c.client.Check(ctx, in)
return getErrors(out, err, ErrCheck)
}
func (c Client) Update(ctx context.Context, in *UpdateRequest) error {
out, err := c.client.Update(ctx, in)
return getErrors(out, err, ErrUpdate)
}
func (c Client) Uninstall(ctx context.Context, in *UninstallRequest) error {
out, err := c.client.Uninstall(ctx, in)
return getErrors(out, err, ErrUninstall)
}
func (c Client) Action(ctx context.Context, in *ActionRequest) (*ActionResponse, error) {
out, err := c.client.Action(ctx, in)
return out, err
}
package extension
import (
"testing"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetErrors(t *testing.T) {
getOut := func(results ...*RequestResult) results { return &InstallResponse{Results: results} }
tests := []struct {
name string
out results
err error
wrapErr error
checkErr func(t *testing.T, err error)
}{
{
name: "no errors",
out: getOut(&RequestResult{State: RequestOK, Extension: "a"}),
},
{
name: "no error",
out: getOut(&RequestResult{State: RequestOK, Extension: "a"}),
wrapErr: ErrInstall,
},
{
name: "one errored result",
out: getOut(&RequestResult{State: RequestError, Extension: "a", Error: "some err", Msg: "Ошибка"}),
wrapErr: ErrInstall,
checkErr: func(t *testing.T, err error) {
assert.ErrorIs(t, err, ErrInstall)
errs := errors.GetErrors(err)
require.Len(t, errs, 1)
extErr := errs[0]
assert.Equal(t, "a", ExtensionFromError(extErr))
assert.Equal(t, "some err", extErr.Error())
assert.Equal(t, "Ошибка", errors.GetDetail(extErr))
},
},
{
name: "multiple results, some of them errored",
out: getOut(
&RequestResult{State: RequestError, Extension: "a", Error: "some err a", Msg: "Ошибка А"},
&RequestResult{State: RequestOK, Extension: "b"},
&RequestResult{State: RequestError, Extension: "c", Error: "some err c", Msg: "Ошибка С"},
&RequestResult{State: RequestOK, Extension: "d"},
),
wrapErr: ErrInstall,
checkErr: func(t *testing.T, err error) {
assert.ErrorIs(t, err, ErrInstall)
errs := errors.GetErrors(err)
require.Len(t, errs, 2)
extErr1 := errs[0]
assert.Equal(t, "a", ExtensionFromError(extErr1))
assert.Equal(t, "some err a", extErr1.Error())
assert.Equal(t, "Ошибка А", errors.GetDetail(extErr1))
extErr2 := errs[1]
assert.Equal(t, "c", ExtensionFromError(extErr2))
assert.Equal(t, "some err c", extErr2.Error())
assert.Equal(t, "Ошибка С", errors.GetDetail(extErr2))
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := getErrors(tt.out, tt.err, tt.wrapErr)
if tt.checkErr != nil {
require.Error(t, err)
tt.checkErr(t, err)
} else {
require.NoError(t, err)
}
})
}
}
package extension
import (
"context"
"fmt"
"git.perx.ru/perxis/perxis-go/pkg/content"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/items"
pb "git.perx.ru/perxis/perxis-go/proto/extensions"
)
const (
RequestOK = pb.ExtensionRequestResult_OK
RequestError = pb.ExtensionRequestResult_ERROR
RequestPending = pb.ExtensionRequestResult_PENDING
RequestInProgress = pb.ExtensionRequestResult_IN_PROGRESS
RequestParametersRequires = pb.ExtensionRequestResult_PARAMETERS_REQUIRED
ExtensionsCollectionID = "space_extensions"
StatePending = pb.SpaceExtensions_PENDING
StateInstalled = pb.SpaceExtensions_INSTALLED
StateInProgress = pb.SpaceExtensions_IN_PROGRESS
StateFail = pb.SpaceExtensions_FAIL
)
type (
InstallRequest = pb.InstallRequest
InstallResponse = pb.InstallResponse
CheckRequest = pb.CheckRequest
CheckResponse = pb.CheckResponse
UpdateRequest = pb.UpdateRequest
UpdateResponse = pb.UpdateResponse
UninstallRequest = pb.UninstallRequest
UninstallResponse = pb.UninstallResponse
RequestResult = pb.ExtensionRequestResult
ActionRequest = pb.ActionRequest
ActionResponse = pb.ActionResponse
ExtensionDescriptor = pb.ExtensionDescriptor
)
var (
ErrStart = errors.New("start failed")
ErrStop = errors.New("stop failed")
ErrInstall = errors.New("install failed")
ErrUpdate = errors.New("update failed")
ErrCheck = errors.New("check failed")
ErrUninstall = errors.New("uninstall failed")
ErrUpdateAvailable = errors.New("update available")
ErrNotInstalled = errors.New("not installed")
ErrUnknownExtension = errors.New("unknown extension")
)
// Runnable описывает интерфейс сервиса с запуском и остановкой. Вызывается сервером расширений
type Runnable interface {
Start() error
Stop() error
}
// Extension описывает интерфейс расширения Perxis
type Extension interface {
// GetDescriptor возвращает описание расширения
GetDescriptor() *ExtensionDescriptor
// Install вызывается при установке расширения в пространство
Install(ctx context.Context, in *InstallRequest) error
// Check вызывается для проверки состояния расширения
Check(ctx context.Context, in *CheckRequest) error
// Update вызывается для обновления вресии расширения
Update(ctx context.Context, in *UpdateRequest) error
// Uninstall вызывается для удаления расширения из пространства
Uninstall(ctx context.Context, in *UninstallRequest) error
// Action вызывается для выполнения расширением действия
Action(ctx context.Context, in *ActionRequest) (*ActionResponse, error)
}
func CheckInstalled(ctx context.Context, content *content.Content, spaceID, envID, extension string) (bool, error) {
res, _, err := content.Items.Find(ctx, spaceID, envID, ExtensionsCollectionID,
&items.Filter{Q: []string{fmt.Sprintf("extension == '%s'", extension)}})
if err != nil {
return false, err
}
if len(res) == 0 || res[0].Data["extension_state"] != int64(StateInstalled) {
return false, nil
}
return true, nil
}
func ExtensionError(err error, ext string) error {
return errors.WithContext(err, "extension", ext)
}
func ExtensionFromError(err error) string {
v, _ := errors.ContextKey(err, "extension")
ext, _ := v.(string)
return ext
}
package extension
import (
"crypto/sha512"
"encoding/base64"
"strings"
)
// KeyFunc функция получения ключа доступа к указанному пространству
type KeyFunc func(spaceID string) string
type SignatureFunc func(spaceID string) string
func NamedSignedKey(name string, signature any) KeyFunc {
switch v := signature.(type) {
case string:
return func(spaceID string) string {
return Sign(name, spaceID, v)
}
case SignatureFunc:
return func(spaceID string) string {
return Sign(name, spaceID, v(spaceID))
}
}
panic("incorrect signature")
}
func ExtensionSignedKey(ext, signature any) KeyFunc {
var desc *ExtensionDescriptor
switch v := ext.(type) {
case Extension:
desc = v.GetDescriptor()
case *ExtensionDescriptor:
desc = v
}
switch v := signature.(type) {
case string:
return func(spaceID string) string {
return Sign(desc.Extension, spaceID, v)
}
case SignatureFunc:
return func(spaceID string) string {
return Sign(desc.Extension, spaceID, v(spaceID))
}
}
panic("incorrect signature")
}
func Sign(vals ...string) string {
value := strings.Join(vals, ".")
hash := sha512.Sum512([]byte(value))
encoded := base64.StdEncoding.EncodeToString(hash[:])
return encoded
}
// Code generated by mockery v2.14.0. DO NOT EDIT.
package mocks
import (
context "context"
extensions "git.perx.ru/perxis/perxis-go/proto/extensions"
mock "github.com/stretchr/testify/mock"
)
// Extension is an autogenerated mock type for the Extension type
type Extension struct {
mock.Mock
}
// Action provides a mock function with given fields: ctx, in
func (_m *Extension) Action(ctx context.Context, in *extensions.ActionRequest) (*extensions.ActionResponse, error) {
ret := _m.Called(ctx, in)
var r0 *extensions.ActionResponse
if rf, ok := ret.Get(0).(func(context.Context, *extensions.ActionRequest) *extensions.ActionResponse); ok {
r0 = rf(ctx, in)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*extensions.ActionResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *extensions.ActionRequest) error); ok {
r1 = rf(ctx, in)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Check provides a mock function with given fields: ctx, in
func (_m *Extension) Check(ctx context.Context, in *extensions.CheckRequest) error {
ret := _m.Called(ctx, in)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *extensions.CheckRequest) error); ok {
r0 = rf(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetDescriptor provides a mock function with given fields:
func (_m *Extension) GetDescriptor() *extensions.ExtensionDescriptor {
ret := _m.Called()
var r0 *extensions.ExtensionDescriptor
if rf, ok := ret.Get(0).(func() *extensions.ExtensionDescriptor); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*extensions.ExtensionDescriptor)
}
}
return r0
}
// Install provides a mock function with given fields: ctx, in
func (_m *Extension) Install(ctx context.Context, in *extensions.InstallRequest) error {
ret := _m.Called(ctx, in)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *extensions.InstallRequest) error); ok {
r0 = rf(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
// Uninstall provides a mock function with given fields: ctx, in
func (_m *Extension) Uninstall(ctx context.Context, in *extensions.UninstallRequest) error {
ret := _m.Called(ctx, in)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *extensions.UninstallRequest) error); ok {
r0 = rf(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
// Update provides a mock function with given fields: ctx, in
func (_m *Extension) Update(ctx context.Context, in *extensions.UpdateRequest) error {
ret := _m.Called(ctx, in)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *extensions.UpdateRequest) error); ok {
r0 = rf(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
type mockConstructorTestingTNewExtension interface {
mock.TestingT
Cleanup(func())
}
// NewExtension creates a new instance of Extension. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewExtension(t mockConstructorTestingTNewExtension) *Extension {
mock := &Extension{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
package extension
import (
"context"
"git.perx.ru/perxis/perxis-go/pkg/errors"
pb "git.perx.ru/perxis/perxis-go/proto/extensions"
)
type Server struct {
services map[string]Extension
pb.UnimplementedExtensionServer
}
func NewServer(svc ...Extension) *Server {
srv := &Server{
services: make(map[string]Extension, len(svc)),
}
for _, s := range svc {
srv.services[s.GetDescriptor().Extension] = s
}
return srv
}
func getResult(ext string, err error) *RequestResult {
res := new(RequestResult)
res.Extension = ext
res.State = RequestOK
if err != nil {
res.State = RequestError
res.Error = err.Error()
errs := errors.GetErrors(err)
if errs == nil {
errs = append(errs, err)
}
for _, e := range errs {
res.Msg += errors.GetDetail(e) + "\n"
}
}
return res
}
func (srv *Server) getResults(extensions []string, fn func(svc Extension) error) []*RequestResult {
var results []*RequestResult
for _, e := range extensions {
svc, ok := srv.services[e]
if !ok {
results = append(results, getResult(e, ErrUnknownExtension))
continue
}
err := fn(svc)
results = append(results, getResult(e, err))
}
return results
}
func (srv *Server) Install(ctx context.Context, request *InstallRequest) (*InstallResponse, error) {
res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Install(ctx, request) })
return &InstallResponse{Results: res}, nil
}
func (srv *Server) Check(ctx context.Context, request *CheckRequest) (*CheckResponse, error) {
res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Check(ctx, request) })
return &CheckResponse{Results: res}, nil
}
func (srv *Server) Uninstall(ctx context.Context, request *UninstallRequest) (*UninstallResponse, error) {
res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Uninstall(ctx, request) })
return &UninstallResponse{Results: res}, nil
}
func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateResponse, error) {
res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Update(ctx, request) })
return &UpdateResponse{Results: res}, nil
}
func (srv *Server) Action(ctx context.Context, in *ActionRequest) (*ActionResponse, error) {
svc, ok := srv.services[in.Extension]
if !ok {
return nil, ErrUnknownExtension
}
out, err := svc.Action(ctx, in)
if out == nil {
out = &ActionResponse{}
}
if err != nil {
out.State = ResponseError
out.Error = err.Error()
out.Msg += errors.GetDetail(err)
}
return out, nil
}
func (srv *Server) Start() error {
var errs []error
for _, svc := range srv.services {
if r, ok := svc.(Runnable); ok {
if err := r.Start(); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrStart, errs...)
}
return nil
}
func (srv *Server) Stop() error {
var errs []error
for _, svc := range srv.services {
if r, ok := svc.(Runnable); ok {
if err := r.Stop(); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrStop, errs...)
}
return nil
}
package extension
import (
"reflect"
"strings"
"testing"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/extension/mocks"
"github.com/stretchr/testify/mock"
)
func TestGetResults(t *testing.T) {
getMockExtension := func(name string, wantErr ...bool) Extension {
ext := &mocks.Extension{}
ext.On("GetDescriptor").Return(&ExtensionDescriptor{
Extension: name,
Title: strings.ToTitle(name),
Description: "test extension",
Version: "0.0.0",
})
var err error
if len(wantErr) > 0 {
err = errors.WithDetail(errors.New("some err"), "Ошибка")
}
ext.On("Install", mock.Anything, mock.Anything).Return(err)
return ext
}
tests := []struct {
name string
services []Extension
extensions []string
fn func(svc Extension) error
want []*RequestResult
}{
{
name: "one extension without errors",
services: []Extension{getMockExtension("a"), getMockExtension("b")},
extensions: []string{"a"},
fn: func(svc Extension) error { return nil },
want: []*RequestResult{
{Extension: "a", State: RequestOK},
},
},
{
name: "multiple extensions without errors",
services: []Extension{getMockExtension("a"), getMockExtension("b"), getMockExtension("c")},
extensions: []string{"a", "c"},
fn: func(svc Extension) error { return nil },
want: []*RequestResult{
{Extension: "a", State: RequestOK},
{Extension: "c", State: RequestOK},
},
},
{
name: "multiple extensions, one returns error",
services: []Extension{getMockExtension("a"), getMockExtension("b"), getMockExtension("c", true)},
extensions: []string{"a", "c"},
fn: func(svc Extension) error { return svc.Install(nil, nil) },
want: []*RequestResult{
{Extension: "a", State: RequestOK},
{Extension: "c", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
},
},
{
name: "multiple extensions, all return error",
services: []Extension{getMockExtension("a", true), getMockExtension("b", true), getMockExtension("c", true)},
extensions: []string{"a", "b", "c"},
fn: func(svc Extension) error { return svc.Install(nil, nil) },
want: []*RequestResult{
{Extension: "a", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
{Extension: "b", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
{Extension: "c", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
srv := NewServer(tt.services...)
if got := srv.getResults(tt.extensions, tt.fn); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getResults() = %v, want %v", got, tt.want)
}
})
}
}
package setup
import (
"context"
"strings"
"git.perx.ru/perxis/perxis-go/pkg/clients"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"go.uber.org/zap"
)
var (
ErrCheckClients = errors.New("clients check error")
ErrInstallClients = errors.New("failed to install clients")
ErrUninstallClients = errors.New("failed to uninstall clients")
)
type ClientsOption func(c *ClientConfig)
type UpdateClientFn func(s *Setup, exist, new *clients.Client) (*clients.Client, bool)
type DeleteClientFn func(s *Setup, client *clients.Client) bool
type ClientConfig struct {
client *clients.Client
UpdateFn UpdateClientFn
DeleteFn DeleteClientFn
}
func NewClientConfig(client *clients.Client, opt ...ClientsOption) ClientConfig {
c := ClientConfig{client: client}
UpdateExistingClient()(&c)
DeleteClientIfRemove()(&c)
for _, o := range opt {
o(&c)
}
return c
}
func OverwriteClient() ClientsOption {
return func(c *ClientConfig) {
c.UpdateFn = func(s *Setup, old, new *clients.Client) (*clients.Client, bool) { return new, true }
}
}
func KeepExistingClient() ClientsOption {
return func(c *ClientConfig) {
c.UpdateFn = func(s *Setup, old, new *clients.Client) (*clients.Client, bool) { return old, false }
}
}
func DeleteClient() ClientsOption {
return func(c *ClientConfig) {
c.DeleteFn = func(s *Setup, client *clients.Client) bool { return true }
}
}
func DeleteClientIfRemove() ClientsOption {
return func(c *ClientConfig) {
c.DeleteFn = func(s *Setup, client *clients.Client) bool { return s.IsRemove() }
}
}
func UpdateExistingClient() ClientsOption {
return func(c *ClientConfig) {
c.UpdateFn = func(s *Setup, exist, client *clients.Client) (*clients.Client, bool) {
if exist.Name == "" {
exist.Name = client.Name
}
if exist.Description == "" {
exist.Description = client.Description
}
if exist.OAuth == nil {
exist.OAuth = client.OAuth
}
if exist.TLS == nil {
exist.TLS = client.TLS
}
if exist.APIKey == nil {
exist.APIKey = client.APIKey
}
exist.Disabled = client.Disabled
exist.RoleID = client.RoleID
return exist, true
}
}
}
func (s *Setup) InstallClients(ctx context.Context) error {
if len(s.Clients) == 0 {
return nil
}
s.logger.Debug("Install clients", zap.String("Space ID", s.SpaceID))
var errs []error
for _, c := range s.Clients {
err := s.InstallClient(ctx, c)
if err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при настройке клиента %s(%s)", c.client.Name, c.client.ID))
s.logger.Error("Failed to install client", zap.String("Client ID", c.client.ID), zap.String("Client Name", c.client.Name), zap.Error(err))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrInstallClients, errs...)
}
return nil
}
func (s *Setup) InstallClient(ctx context.Context, c ClientConfig) error {
client := c.client
client.SpaceID = s.SpaceID
if s.IsForce() {
s.content.Clients.Delete(ctx, s.SpaceID, c.client.ID)
_, err := s.content.Clients.Create(ctx, c.client)
return err
}
exist, err := s.content.Clients.Get(ctx, s.SpaceID, c.client.ID)
if err != nil {
if !strings.Contains(err.Error(), clients.ErrNotFound.Error()) {
return err
}
_, err = s.content.Clients.Create(ctx, c.client)
return err
}
if client, upd := c.UpdateFn(s, exist, c.client); upd {
return s.content.Clients.Update(ctx, client)
}
return nil
}
func (s *Setup) CheckClients(ctx context.Context) (err error) {
if len(s.Clients) == 0 {
return nil
}
var errs []error
s.logger.Debug("Check clients", zap.String("Space ID", s.SpaceID))
for _, c := range s.Clients {
err := s.CheckClient(ctx, c.client)
if err != nil {
errs = append(errs, errors.WithDetailf(err, "Не найден клиент %s(%s)", c.client.Name, c.client.ID))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrCheckClients, errs...)
}
return nil
}
func (s *Setup) CheckClient(ctx context.Context, client *clients.Client) error {
_, err := s.content.Clients.Get(ctx, s.SpaceID, client.ID)
return err
}
func (s *Setup) UninstallClients(ctx context.Context) error {
if len(s.Clients) == 0 {
return nil
}
s.logger.Debug("Uninstall clients", zap.String("Space ID", s.SpaceID))
var errs []error
for _, c := range s.Clients {
if err := s.UninstallClient(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при удалении клиента %s(%s)", c.client.Name, c.client.ID))
s.logger.Error("Failed to uninstall client", zap.String("Client ID", c.client.ID), zap.String("Client Name", c.client.Name), zap.Error(err))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrUninstallClients, errs...)
}
return nil
}
func (s *Setup) UninstallClient(ctx context.Context, c ClientConfig) error {
if c.DeleteFn(s, c.client) {
return s.content.Clients.Delete(ctx, s.SpaceID, c.client.ID)
}
return nil
}
package setup
import (
"context"
"reflect"
"strings"
"git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/environments"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"go.uber.org/zap"
)
var (
ErrCheckCollections = errors.New("collections check error")
ErrInstallCollections = errors.New("failed to install collections")
ErrUninstallCollections = errors.New("failed to uninstall collections")
)
type CollectionsOption func(c *CollectionConfig)
type UpdateCollectionFn func(s *Setup, exist, new *collections.Collection) (coll *collections.Collection, upd bool, setSchema bool)
type DeleteCollectionFn func(s *Setup, col *collections.Collection) bool
type CollectionConfig struct {
collection *collections.Collection
UpdateFn UpdateCollectionFn
DeleteFn DeleteCollectionFn
}
func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsOption) CollectionConfig {
c := CollectionConfig{collection: collection}
UpdateExistingCollection()(&c)
DeleteCollectionIfRemove()(&c)
for _, o := range opt {
o(&c)
}
return c
}
func OverwriteCollection() CollectionsOption {
return func(c *CollectionConfig) {
c.UpdateFn = func(s *Setup, old, new *collections.Collection) (*collections.Collection, bool, bool) {
return new, true, true
}
}
}
func KeepExistingCollection() CollectionsOption {
return func(c *CollectionConfig) {
c.UpdateFn = func(s *Setup, old, new *collections.Collection) (*collections.Collection, bool, bool) {
return old, false, false
}
}
}
func DeleteCollection() CollectionsOption {
return func(c *CollectionConfig) {
c.DeleteFn = func(s *Setup, collection *collections.Collection) bool { return true }
}
}
func DeleteCollectionIfRemove() CollectionsOption {
return func(c *CollectionConfig) {
c.DeleteFn = func(s *Setup, collection *collections.Collection) bool { return s.IsRemove() }
}
}
func UpdateExistingCollection() CollectionsOption {
return func(c *CollectionConfig) {
c.UpdateFn = func(s *Setup, exist, collection *collections.Collection) (*collections.Collection, bool, bool) {
if len(exist.Tags) > 0 {
collection.Tags = append(exist.Tags, collection.Tags...)
}
return collection, true, !collection.IsView() && !reflect.DeepEqual(exist.Schema, collection.Schema)
}
}
}
func (s *Setup) InstallCollections(ctx context.Context) (err error) {
if len(s.Collections) == 0 {
return nil
}
s.logger.Debug("Install collections", zap.Int("Collections", len(s.Collections)))
var errs []error
var migrate, setSchema bool
for _, c := range s.Collections {
setSchema, err = s.InstallCollection(ctx, c)
if err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при настройке коллекции %s(%s)", c.collection.Name, c.collection.ID))
s.logger.Error("Failed to install collection",
zap.String("Collection ID", c.collection.ID),
zap.String("Collection Name", c.collection.Name),
zap.Error(err),
)
}
if setSchema {
migrate = true
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrInstallCollections, errs...)
}
if migrate {
if err = s.content.Environments.Migrate(ctx, s.SpaceID, s.EnvironmentID, &environments.MigrateOptions{Wait: true}); err != nil {
s.logger.Error(
"Failed to migrate environment",
zap.String("Space ID", s.SpaceID),
zap.String("Environment ID", s.EnvironmentID),
zap.Error(err),
)
return errors.WithErrors(ErrInstallCollections, err)
}
}
return nil
}
func (s *Setup) InstallCollection(ctx context.Context, c CollectionConfig) (setSchema bool, err error) {
collection := c.collection
collection.SpaceID, collection.EnvID = s.SpaceID, s.EnvironmentID
var exist *collections.Collection
// isForce - не удалять коллекцию, если она уже существует
exist, err = s.content.Collections.Get(ctx, collection.SpaceID, collection.EnvID, collection.ID)
if err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
return false, err
}
if exist == nil {
setSchema = !collection.IsView()
exist, err = s.content.Collections.Create(ctx, collection)
if err != nil {
return false, err
}
} else {
var upd bool
collection, upd, setSchema = c.UpdateFn(s, exist, c.collection)
if upd {
if err = s.content.Collections.Update(ctx, collection); err != nil {
return false, err
}
}
}
if setSchema {
err = s.content.Collections.SetSchema(ctx, collection.SpaceID, collection.EnvID, collection.ID, collection.Schema)
if err != nil {
return false, err
}
}
return setSchema, nil
}
func (s *Setup) CheckCollections(ctx context.Context) error {
if len(s.Collections) == 0 {
return nil
}
s.logger.Debug("Check collections", zap.Int("Collections", len(s.Collections)))
var errs []error
for _, c := range s.Collections {
if err := s.CheckCollection(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Не найдена коллекция %s(%s)", c.collection.ID, c.collection.Name))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrCheckCollections, errs...)
}
return nil
}
func (s *Setup) CheckCollection(ctx context.Context, c CollectionConfig) (err error) {
_, err = s.content.Collections.Get(ctx, s.SpaceID, s.EnvironmentID, c.collection.ID)
return err
}
func (s *Setup) UninstallCollections(ctx context.Context) error {
if len(s.Collections) == 0 {
return nil
}
s.logger.Debug("Uninstall collections", zap.Int("Collections", len(s.Collections)))
var errs []error
for _, c := range s.Collections {
if err := s.UninstallCollection(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при удалении коллекции %s(%s)", c.collection.Name, c.collection.ID))
s.logger.Error("Failed to uninstall collection",
zap.String("Collection ID", c.collection.ID),
zap.String("Collection Name", c.collection.Name),
zap.Error(err),
)
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrUninstallCollections, errs...)
}
return nil
}
func (s *Setup) UninstallCollection(ctx context.Context, c CollectionConfig) error {
if c.DeleteFn(s, c.collection) {
if err := s.content.Collections.Delete(ctx, s.SpaceID, s.EnvironmentID, c.collection.ID); err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
return err
}
s.removeItems(c.collection.ID) // после удаления коллекции нет смысла удалять ее элементы
}
return nil
}
package setup
import (
"context"
"strings"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/items"
"go.uber.org/zap"
)
var (
ErrCheckItems = errors.New("items check error")
ErrInstallItems = errors.New("failed to install items")
ErrUninstallItems = errors.New("failed to uninstall items")
ErrItemsNotFound = errors.New("item not found")
)
type ItemsOption func(c *ItemConfig)
type UpdateItemFn func(s *Setup, exist, new *items.Item) (*items.Item, bool)
type DeleteItemFn func(s *Setup, col *items.Item) bool
type ItemConfig struct {
item *items.Item
UpdateFn UpdateItemFn
DeleteFn DeleteItemFn
}
func NewItemConfig(item *items.Item, opt ...ItemsOption) ItemConfig {
c := ItemConfig{item: item}
KeepExistingItem()(&c)
DeleteItemIfRemove()(&c)
for _, o := range opt {
o(&c)
}
return c
}
func OverwriteItem() ItemsOption {
return func(c *ItemConfig) {
c.UpdateFn = func(s *Setup, old, new *items.Item) (*items.Item, bool) { return new, true }
}
}
func KeepExistingItem() ItemsOption {
return func(c *ItemConfig) {
c.UpdateFn = func(s *Setup, old, new *items.Item) (*items.Item, bool) { return old, false }
}
}
func DeleteItem() ItemsOption {
return func(c *ItemConfig) {
c.DeleteFn = func(s *Setup, item *items.Item) bool { return true }
}
}
func DeleteItemIfRemove() ItemsOption {
return func(c *ItemConfig) {
c.DeleteFn = func(s *Setup, item *items.Item) bool { return s.IsRemove() }
}
}
func (s *Setup) InstallItems(ctx context.Context) error {
if len(s.Items) == 0 {
return nil
}
s.logger.Debug("Install items", zap.Int("Items", len(s.Items)))
var errs []error
for _, c := range s.Items {
if err := s.InstallItem(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при добавлении элемента %s(%s)", c.item.ID, c.item.CollectionID))
s.logger.Error("Failed to install item",
zap.String("Item", c.item.ID),
zap.String("Item", c.item.CollectionID),
zap.Error(err),
)
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrInstallItems, errs...)
}
return nil
}
func (s *Setup) InstallItem(ctx context.Context, c ItemConfig) error {
item := c.item
item.SpaceID, item.EnvID = s.SpaceID, s.EnvironmentID
exist, err := s.content.Items.Get(ctx, item.SpaceID, item.EnvID, item.CollectionID, item.ID)
if err != nil && !strings.Contains(err.Error(), items.ErrNotFound.Error()) {
return err
}
if exist == nil {
return s.CreateAndPublishItem(ctx, item)
}
if item, changed := c.UpdateFn(s, exist, c.item); changed {
return s.UpdateAndPublishItem(ctx, item)
}
return nil
}
func (s *Setup) UninstallItems(ctx context.Context) error {
if len(s.Items) == 0 {
return nil
}
s.logger.Debug("Uninstall items", zap.Int("Items", len(s.Items)))
var errs []error
for _, c := range s.Items {
if err := s.UninstallItem(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при удалении элемента %s(%s)", c.item.ID, c.item.CollectionID))
s.logger.Error("Failed to uninstall item",
zap.String("Item", c.item.ID),
zap.String("Item", c.item.CollectionID),
zap.Error(err),
)
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrUninstallItems, errs...)
}
return nil
}
func (s *Setup) UninstallItem(ctx context.Context, c ItemConfig) error {
if c.DeleteFn(s, c.item) {
err := s.content.Items.Delete(ctx, s.SpaceID, s.EnvironmentID, c.item.CollectionID, c.item.ID)
if err != nil && !strings.Contains(err.Error(), items.ErrNotFound.Error()) {
return err
}
}
return nil
}
func (s *Setup) CheckItems(ctx context.Context) error {
if len(s.Items) == 0 {
return nil
}
var errs []error
s.logger.Debug("Check items", zap.Int("Items", len(s.Items)))
for _, c := range s.Items {
if err := s.CheckItem(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Не найден элемент %s(%s)", c.item.ID, c.item.CollectionID))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrCheckItems, errs...)
}
return nil
}
func (s *Setup) CheckItem(ctx context.Context, c ItemConfig) (err error) {
_, err = s.content.Items.Get(ctx, s.SpaceID, s.EnvironmentID, c.item.CollectionID, c.item.ID)
return err
}
func (s *Setup) CreateAndPublishItem(ctx context.Context, item *items.Item) error {
var err error
if item, err = s.content.Items.Create(ctx, item); err != nil {
return errors.Wrap(err, "create item")
}
if err = s.content.Items.Publish(ctx, item); err != nil {
return errors.Wrap(err, "publish item")
}
return nil
}
func (s *Setup) UpdateAndPublishItem(ctx context.Context, item *items.Item) error {
var err error
if err = s.content.Items.Update(ctx, item); err != nil {
return errors.Wrap(err, "update item")
}
if err = s.content.Items.Publish(ctx, item); err != nil {
return errors.Wrap(err, "publish item")
}
return nil
}
func (s *Setup) removeItems(collID string) {
itms := make([]ItemConfig, 0, len(s.Items))
for _, i := range s.Items {
if i.item.CollectionID != collID {
itms = append(itms, i)
}
}
s.Items = itms
}
package setup
import (
"context"
"strings"
"git.perx.ru/perxis/perxis-go/pkg/data"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/permission"
"git.perx.ru/perxis/perxis-go/pkg/roles"
"go.uber.org/zap"
)
var (
ErrCheckRoles = errors.New("role check error")
ErrInstallRoles = errors.New("failed to install role")
ErrUninstallRoles = errors.New("failed to uninstall role")
)
type RolesOption func(c *RoleConfig)
type UpdateRoleFn func(s *Setup, exist, new *roles.Role) (*roles.Role, bool)
type DeleteRoleFn func(s *Setup, role *roles.Role) bool
type RoleConfig struct {
role *roles.Role
UpdateFn UpdateRoleFn
DeleteFn DeleteRoleFn
}
func NewRoleConfig(role *roles.Role, opt ...RolesOption) RoleConfig {
c := RoleConfig{role: role}
UpdateExistingRole()(&c)
DeleteRoleIfRemove()(&c)
for _, o := range opt {
o(&c)
}
return c
}
func OverwriteRole() RolesOption {
return func(c *RoleConfig) {
c.UpdateFn = func(s *Setup, old, new *roles.Role) (*roles.Role, bool) { return new, true }
}
}
//func OverwriteRoleIfChanged() RolesOption {
// return func(c *RoleConfig) {
// c.UpdateFn = func(s *Setup, old, new *roles.Role) (*roles.Role, bool) {
// changed := old.Description != new.Description || old.AllowManagement != new.AllowManagement ||
// !data.ElementsMatch(old.Environments, new.Environments)
// return new, changed
// }
// }
//}
func KeepExistingRole() RolesOption {
return func(c *RoleConfig) {
c.UpdateFn = func(s *Setup, old, new *roles.Role) (*roles.Role, bool) { return old, false }
}
}
func DeleteRole() RolesOption {
return func(c *RoleConfig) {
c.DeleteFn = func(s *Setup, role *roles.Role) bool { return true }
}
}
func DeleteRoleIfRemove() RolesOption {
return func(c *RoleConfig) {
c.DeleteFn = func(s *Setup, role *roles.Role) bool { return s.IsRemove() }
}
}
func UpdateExistingRole() RolesOption {
return func(c *RoleConfig) {
c.UpdateFn = func(s *Setup, exist, new *roles.Role) (*roles.Role, bool) {
// если передан флаг force, то обновляем все поля роли на переданные
if s.IsForce() {
return new, true
}
if exist.Description == "" {
exist.Description = new.Description
}
if len(exist.Environments) == 0 {
exist.Environments = new.Environments
}
if !data.Contains(s.EnvironmentID, exist.Environments) {
exist.Environments = append(exist.Environments, s.EnvironmentID)
}
exist.Rules = permission.MergeRules(exist.Rules, new.Rules)
if !exist.AllowManagement {
exist.AllowManagement = new.AllowManagement
}
return exist, true
}
}
}
func (s *Setup) InstallRoles(ctx context.Context) error {
if len(s.Roles) == 0 {
return nil
}
s.logger.Debug("Install role", zap.String("Space ID", s.SpaceID), zap.Int("Roles", len(s.Roles)))
var errs []error
for _, c := range s.Roles {
if err := s.InstallRole(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при настройке роли %s(%s)", c.role.ID, c.role.Description))
s.logger.Error("Failed to install role", zap.String("Role ID", c.role.ID), zap.Error(err))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrInstallRoles, errs...)
}
return nil
}
func (s *Setup) InstallRole(ctx context.Context, c RoleConfig) error {
role := c.role
role.SpaceID = s.SpaceID
if !data.Contains(s.EnvironmentID, c.role.Environments) {
role.Environments = append(role.Environments, s.EnvironmentID)
}
exist, err := s.content.Roles.Get(ctx, s.SpaceID, role.ID)
if err != nil {
if !strings.Contains(err.Error(), roles.ErrNotFound.Error()) {
return err
}
_, err = s.content.Roles.Create(ctx, role)
return err
}
if r, upd := c.UpdateFn(s, exist, role); upd {
return s.content.Roles.Update(ctx, r)
}
return nil
}
func (s *Setup) UninstallRoles(ctx context.Context) error {
if len(s.Roles) == 0 {
return nil
}
s.logger.Debug("Uninstall role", zap.String("Space ID", s.SpaceID), zap.Int("Roles", len(s.Roles)))
var errs []error
for _, c := range s.Roles {
if err := s.UninstallRole(ctx, c); err != nil {
errs = append(errs, errors.WithDetailf(err, "Возникла ошибка при удалении роли %s(%s)", c.role.ID, c.role.Description))
s.logger.Error("Failed to uninstall role", zap.String("Role ID", c.role.ID), zap.Error(err))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrUninstallRoles, errs...)
}
return nil
}
func (s *Setup) UninstallRole(ctx context.Context, c RoleConfig) error {
if c.DeleteFn(s, c.role) {
err := s.content.Roles.Delete(ctx, s.SpaceID, c.role.ID)
if err != nil && !strings.Contains(err.Error(), roles.ErrNotFound.Error()) {
return err
}
}
return nil
}
func (s *Setup) CheckRoles(ctx context.Context) error {
if len(s.Roles) == 0 {
return nil
}
s.logger.Debug("Check role", zap.String("Space ID", s.SpaceID))
var errs []error
for _, c := range s.Roles {
if err := s.CheckRole(ctx, c.role); err != nil {
errs = append(errs, errors.WithDetailf(err, "Не найдена роль %s(%s)", c.role.ID, c.role.Description))
}
}
if len(errs) > 0 {
return errors.WithErrors(ErrCheckRoles, errs...)
}
return nil
}
func (s *Setup) CheckRole(ctx context.Context, role *roles.Role) error {
_, err := s.content.Roles.Get(ctx, s.SpaceID, role.ID)
return err
}
package setup
import (
"context"
"git.perx.ru/perxis/perxis-go/pkg/clients"
"git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/content"
"git.perx.ru/perxis/perxis-go/pkg/items"
"git.perx.ru/perxis/perxis-go/pkg/roles"
"go.uber.org/zap"
)
// Setup реализует процесс настройки пространства. Указав необходимые требования к конфигурации пространства можно
// выполнить процесс установки, проверки и удаления требований.
type Setup struct {
SpaceID string
EnvironmentID string
Roles []RoleConfig
Clients []ClientConfig
Collections []CollectionConfig
Items []ItemConfig
content *content.Content
force bool
remove bool
errors []error
logger *zap.Logger
}
func NewSetup(content *content.Content, spaceID, environmentID string, logger *zap.Logger) *Setup {
//logger = logger.With(zap.String("Space", spaceID), zap.String("Environment", environmentID))
return &Setup{
SpaceID: spaceID,
EnvironmentID: environmentID,
content: content,
logger: logger,
}
}
func (s *Setup) WithForce(force bool) *Setup {
setup := *s
setup.force = force
return &setup
}
func (s *Setup) IsForce() bool {
return s.force
}
func (s *Setup) WithRemove(remove bool) *Setup {
setup := *s
setup.remove = remove
return &setup
}
func (s *Setup) IsRemove() bool {
return s.remove
}
func (s *Setup) HasErrors() bool {
return len(s.errors) > 0
}
func (s *Setup) AddError(err error) {
s.errors = append(s.errors, err)
}
// AddRoles добавляет требования к настройке ролей в пространстве
func (s *Setup) AddRoles(roles []*roles.Role, opt ...RolesOption) *Setup {
for _, role := range roles {
s.AddRole(role, opt...)
}
return s
}
func (s *Setup) AddRole(role *roles.Role, opt ...RolesOption) *Setup {
s.Roles = append(s.Roles, NewRoleConfig(role, opt...))
return s
}
// AddClients добавляет требования к настройке приложений в пространстве
func (s *Setup) AddClients(clients []*clients.Client, opt ...ClientsOption) *Setup {
for _, client := range clients {
s.AddClient(client, opt...)
}
return s
}
func (s *Setup) AddClient(client *clients.Client, opt ...ClientsOption) *Setup {
s.Clients = append(s.Clients, NewClientConfig(client, opt...))
return s
}
// AddCollections добавляет требования к настройке коллекций в пространстве
func (s *Setup) AddCollections(collections []*collections.Collection, opt ...CollectionsOption) *Setup {
for _, col := range collections {
s.AddCollection(col, opt...)
}
return s
}
func (s *Setup) AddCollection(collection *collections.Collection, opt ...CollectionsOption) *Setup {
s.Collections = append(s.Collections, NewCollectionConfig(collection, opt...))
return s
}
// AddItems добавляет требования к настройке элементов в пространстве
func (s *Setup) AddItems(items []*items.Item, opt ...ItemsOption) *Setup {
for _, item := range items {
s.AddItem(item, opt...)
}
return s
}
func (s *Setup) AddItem(item *items.Item, opt ...ItemsOption) *Setup {
s.Items = append(s.Items, NewItemConfig(item, opt...))
return s
}
// Install выполняет установку необходимых требований
func (s *Setup) Install(ctx context.Context) error {
s.logger = s.logger.With(zap.String("Space", s.SpaceID), zap.String("Environment", s.EnvironmentID))
if err := s.InstallRoles(ctx); err != nil {
return err
}
if err := s.InstallClients(ctx); err != nil {
return err
}
if err := s.InstallCollections(ctx); err != nil {
return err
}
if err := s.InstallItems(ctx); err != nil {
return err
}
return nil
}
// Check выполняет проверку требований
func (s *Setup) Check(ctx context.Context) error {
s.logger = s.logger.With(zap.String("Space", s.SpaceID), zap.String("Environment", s.EnvironmentID))
if err := s.CheckRoles(ctx); err != nil {
return err
}
if err := s.CheckClients(ctx); err != nil {
return err
}
if err := s.CheckCollections(ctx); err != nil {
return err
}
if err := s.CheckItems(ctx); err != nil {
return err
}
return nil
}
// Uninstall выполняет удаление установленных раннее требований
func (s *Setup) Uninstall(ctx context.Context) error {
s.logger = s.logger.With(zap.String("Space", s.SpaceID), zap.String("Environment", s.EnvironmentID))
// В случае если необходимо удалить данные удаляем все что создано при установке расширения
if err := s.UninstallClients(ctx); err != nil {
return err
}
if err := s.UninstallRoles(ctx); err != nil {
return err
}
if err := s.UninstallCollections(ctx); err != nil {
return err
}
if err := s.UninstallItems(ctx); err != nil {
return err
}
return nil
}
package setup
import (
"context"
"errors"
"testing"
"git.perx.ru/perxis/perxis-go/pkg/clients"
clientsMock "git.perx.ru/perxis/perxis-go/pkg/clients/mocks"
"git.perx.ru/perxis/perxis-go/pkg/collections"
collectionMock "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
"git.perx.ru/perxis/perxis-go/pkg/content"
"git.perx.ru/perxis/perxis-go/pkg/data"
"git.perx.ru/perxis/perxis-go/pkg/environments"
environmentMock "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
"git.perx.ru/perxis/perxis-go/pkg/extension"
"git.perx.ru/perxis/perxis-go/pkg/items"
itemsMock "git.perx.ru/perxis/perxis-go/pkg/items/mocks"
"git.perx.ru/perxis/perxis-go/pkg/roles"
rolesMock "git.perx.ru/perxis/perxis-go/pkg/roles/mocks"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
const (
spaceID = "sp"
envID = "env"
)
func getCollections() []*collections.Collection {
return []*collections.Collection{
{
ID: "coll1",
SpaceID: spaceID,
EnvID: envID,
Schema: schema.New(),
Name: "Коллекция",
},
}
}
func getRoles() []*roles.Role {
return []*roles.Role{
{
ID: "role",
SpaceID: spaceID,
},
}
}
func getClients() []*clients.Client {
return []*clients.Client{
{
ID: "client",
SpaceID: spaceID,
RoleID: "role",
},
}
}
func getActions() []*items.Item {
return []*items.Item{
{
ID: "act",
SpaceID: spaceID,
EnvID: envID,
CollectionID: extension.ActionsCollectionID,
Data: map[string]interface{}{
"action": "act",
"name": "Action",
"extension": "ext",
},
},
}
}
func newSetup(content *content.Content, t *testing.T) *Setup {
logger := zaptest.NewLogger(t, zaptest.WrapOptions())
setup := NewSetup(content, spaceID, envID, logger)
setup.AddCollections(getCollections())
setup.AddRoles(getRoles())
setup.AddClients(getClients())
setup.AddItems(getActions(), OverwriteItem())
return setup
}
func TestSetupInstall(t *testing.T) {
t.Run("Success, nothing to install", func(t *testing.T) {
logger := zaptest.NewLogger(t, zaptest.WrapOptions())
setup := NewSetup(nil, spaceID, envID, logger)
err := setup.Install(context.Background())
require.NoError(t, err)
})
t.Run("Success, no force", func(t *testing.T) {
envMocks := &environmentMock.Environments{}
envMocks.On("Migrate", mock.Anything, spaceID, envID, &environments.MigrateOptions{Wait: true}).
Return(nil).
Once()
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(nil, collections.ErrNotFound).
Once()
collsMock.On("Create", mock.Anything, collection).
Return(collection, nil).
Once()
collsMock.On("SetSchema", mock.Anything, spaceID, envID, collection.ID, collection.Schema).
Return(nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
create := args[1].(*roles.Role)
require.True(t, data.Contains(envID, create.Environments))
}).Return(role, nil).Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, clients.ErrNotFound).
Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(nil, items.ErrNotFound).
Once()
itmMock.On("Create", mock.Anything, act).
Return(act, nil).
Once()
itmMock.On("Publish", mock.Anything, act).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
Environments: envMocks,
}, t)
err := setup.Install(context.Background())
require.NoError(t, err)
require.False(t, setup.HasErrors())
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
itmMock.AssertExpectations(t)
envMocks.AssertExpectations(t)
})
t.Run("Success, update existing records", func(t *testing.T) {
envMocks := &environmentMock.Environments{}
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(collection, nil).
Once()
collsMock.On("Update", mock.Anything, collection).
Return(nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(role, nil).
Once()
rMock.On("Update", mock.Anything, role).
Return(nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(client, nil).
Once()
clMock.On("Update", mock.Anything, client).
Return(nil).
Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(act, nil).
Once()
itmMock.On("Update", mock.Anything, mock.Anything).
Return(nil).
Once()
itmMock.On("Publish", mock.Anything, mock.Anything).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
Environments: envMocks,
}, t)
err := setup.Install(context.Background())
require.NoError(t, err)
require.False(t, setup.HasErrors())
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
itmMock.AssertExpectations(t)
envMocks.AssertExpectations(t)
})
t.Run("Success, with force", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(collection, nil).
Once()
collsMock.On("Update", mock.Anything, collection).
Return(nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).Return(role, nil).Once()
rMock.On("Update", mock.Anything, &roles.Role{ID: "role", SpaceID: "sp", Environments: []string{"env"}}).Return(nil).Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Delete", mock.Anything, spaceID, client.ID).Return(nil).Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(nil, items.ErrNotFound).
Once()
itmMock.On("Create", mock.Anything, act).
Return(act, nil).
Once()
itmMock.On("Publish", mock.Anything, act).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
}, t)
setup = setup.WithForce(true)
err := setup.Install(context.Background())
require.NoError(t, err)
require.False(t, setup.HasErrors())
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
itmMock.AssertExpectations(t)
})
t.Run("Can't install role, storage returns error", func(t *testing.T) {
rMock := &rolesMock.Roles{}
rMock.On("Get", mock.Anything, spaceID, mock.Anything).
Return(nil, errors.New("can't get role")).
Once()
setup := newSetup(&content.Content{
Roles: rMock,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install role")
rMock.AssertExpectations(t)
})
t.Run("Can't install client, storage returns error", func(t *testing.T) {
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, errors.New("can't get client")).
Once()
}
setup := newSetup(&content.Content{
Clients: clMock,
Roles: rMock,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install clients")
rMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't get collection, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(nil, errors.New("can't get collection")).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, clients.ErrNotFound).
Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install collections")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't create collection, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(nil, collections.ErrNotFound).
Once()
collsMock.On("Create", mock.Anything, collection).
Return(nil, errors.New("can't create collection")).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, clients.ErrNotFound).
Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install collections")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't update collection, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(collection, nil).
Once()
collsMock.On("Update", mock.Anything, collection).
Return(errors.New("can't update collection")).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, clients.ErrNotFound).
Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install collections")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't set schema, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(nil, collections.ErrNotFound).
Once()
collsMock.On("Create", mock.Anything, collection).
Return(collection, nil).
Once()
collsMock.On("SetSchema", mock.Anything, spaceID, envID, collection.ID, collection.Schema).
Return(errors.New("can't set schema")).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, clients.ErrNotFound).
Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install collections")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't migrate, storage returns error", func(t *testing.T) {
envMocks := &environmentMock.Environments{}
envMocks.On("Migrate", mock.Anything, spaceID, envID, &environments.MigrateOptions{Wait: true}).
Return(errors.New("can't migrate")).
Once()
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(nil, collections.ErrNotFound).
Once()
collsMock.On("Create", mock.Anything, collection).
Return(collection, nil).
Once()
collsMock.On("SetSchema", mock.Anything, spaceID, envID, collection.ID, collection.Schema).
Return(nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(nil, roles.ErrNotFound).
Once()
rMock.On("Create", mock.Anything, mock.Anything).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(nil, clients.ErrNotFound).
Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Environments: envMocks,
}, t)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install collections")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't find action, storage returns error", func(t *testing.T) {
envMocks := &environmentMock.Environments{}
envMocks.On("Migrate", mock.Anything, spaceID, envID, &environments.MigrateOptions{Wait: true}).
Return(nil).
Once()
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(nil, collections.ErrNotFound).
Once()
collsMock.On("Create", mock.Anything, collection).
Return(collection, nil).
Once()
collsMock.On("SetSchema", mock.Anything, spaceID, envID, collection.ID, collection.Schema).
Return(nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).Return(role, nil).Once()
rMock.On("Update", mock.Anything, &roles.Role{ID: "role", SpaceID: "sp", Environments: []string{"env"}}).Return(nil).Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Delete", mock.Anything, spaceID, client.ID).Return(nil).Once()
clMock.On("Create", mock.Anything, client).
Return(client, nil).
Once()
}
itmMock := &itemsMock.Items{}
itmMock.On(
"Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, mock.Anything,
).
Return(nil, items.ErrNotFound).
Once()
itmMock.On("Create", mock.Anything, mock.Anything).
Return(nil, errors.New("can't create item")).
Once()
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
Environments: envMocks,
}, t)
setup = setup.WithForce(true)
err := setup.Install(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to install items")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
itmMock.AssertExpectations(t)
envMocks.AssertExpectations(t)
})
//t.Run("Can't find task configs, storage returns error", func(t *testing.T) {
// envMocks := &environmentMock.Environments{}
// envMocks.On("Migrate", mock.Anything, spaceID, envID).
// Return(nil).
// Once()
//
// collsMock := &collectionMock.Collections{}
// for _, collection := range getCollections() {
// collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
// Return(nil, collections.ErrNotFound).
// Once()
//
// collsMock.On("Create", mock.Anything, collection).
// Return(collection, nil).
// Once()
//
// collsMock.On("SetSchema", mock.Anything, spaceID, envID, collection.ID, collection.Schema).
// Return(nil).
// Once()
// }
//
// rMock := &rolesMock.Roles{}
// for _, role := range getRoles() {
// rMock.On("Get", mock.Anything, spaceID, role.ID).
// Return(nil, roles.ErrNotFound).
// Once()
//
// rMock.On("Create", mock.Anything, mock.Anything).
// Return(role, nil).
// Once()
// }
//
// clMock := &clientsMock.Clients{}
// for _, client := range getClients() {
// clMock.On("Get", mock.Anything, spaceID, client.ID).
// Return(nil, clients.ErrNotFound).
// Once()
//
// clMock.On("Create", mock.Anything, client).
// Return(client, nil).
// Once()
// }
//
// itmMock := &itemsMock.Items{}
// for _, act := range getActions() {
// itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
// Return(nil, items.ErrNotFound).
// Once()
//
// itmMock.On("Create", mock.Anything, act).
// Return(act, nil).
// Once()
// itmMock.On("Publish", mock.Anything, act).
// Return(nil).
// Once()
// }
//
// setup := newSetup(&content.Content{
// Collections: collsMock,
// Clients: clMock,
// Roles: rMock,
// Items: itmMock,
// Environments: envMocks,
// }, t)
//
// err := setup.Install(context.Background())
//
// require.Error(t, err)
// assert.ErrorContains(t, err, "failed to install task configs")
//
// rMock.AssertExpectations(t)
// collsMock.AssertExpectations(t)
// clMock.AssertExpectations(t)
// itmMock.AssertExpectations(t)
// envMocks.AssertExpectations(t)
//})
}
func TestSetupUninstall(t *testing.T) {
t.Run("Success, nothing to uninstall", func(t *testing.T) {
logger := zaptest.NewLogger(t, zaptest.WrapOptions())
setup := NewSetup(nil, spaceID, envID, logger)
err := setup.Uninstall(context.Background())
require.NoError(t, err)
})
t.Run("Remove", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Delete", mock.Anything, spaceID, envID, collection.ID).
Return(nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Delete", mock.Anything, spaceID, role.ID).
Return(nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Delete", mock.Anything, spaceID, client.ID).Return(nil).Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
}, t)
setup = setup.WithRemove(true)
err := setup.Uninstall(context.Background())
require.NoError(t, err)
require.False(t, setup.HasErrors())
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
itmMock.AssertExpectations(t)
})
t.Run("Can't uninstall clients, storage returns error", func(t *testing.T) {
rMock := &rolesMock.Roles{}
clMock := &clientsMock.Clients{}
clMock.On("Delete", mock.Anything, spaceID, mock.Anything).
Return(errors.New("can't delete client")).
Once()
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Clients: clMock,
Roles: rMock,
Items: itmMock,
}, t)
setup = setup.WithRemove(true)
err := setup.Uninstall(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to uninstall clients")
rMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't uninstall role, storage returns error", func(t *testing.T) {
rMock := &rolesMock.Roles{}
rMock.On("Delete", mock.Anything, spaceID, mock.Anything).
Return(errors.New("can't delete role")).
Once()
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Delete", mock.Anything, spaceID, client.ID).Return(nil).Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Roles: rMock,
Clients: clMock,
Items: itmMock,
}, t)
setup = setup.WithRemove(true)
err := setup.Uninstall(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to uninstall role")
rMock.AssertExpectations(t)
})
t.Run("Can't uninstall collections, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
collsMock.On("Delete", mock.Anything, spaceID, envID, mock.Anything).
Return(errors.New("can't delete collection")).
Once()
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Delete", mock.Anything, spaceID, role.ID).
Return(nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Delete", mock.Anything, spaceID, client.ID).Return(nil).Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
}, t)
setup = setup.WithRemove(true)
err := setup.Uninstall(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to uninstall collections")
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't uninstall actions, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
collsMock.On("Delete", mock.Anything, spaceID, envID, mock.Anything).
Return(nil).
Once()
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Delete", mock.Anything, spaceID, role.ID).
Return(nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Delete", mock.Anything, spaceID, client.ID).Return(nil).Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(errors.New("can't delete item")).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
}, t)
setup = setup.WithRemove(true)
err := setup.Uninstall(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "failed to uninstall items")
itmMock.AssertExpectations(t)
})
}
func TestSetupCheck(t *testing.T) {
t.Run("Success, nothing to check", func(t *testing.T) {
logger := zaptest.NewLogger(t, zaptest.WrapOptions())
setup := NewSetup(nil, spaceID, envID, logger)
err := setup.Check(context.Background())
require.NoError(t, err)
})
t.Run("Success", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(collection, nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(client, nil).
Once()
}
itmMock := &itemsMock.Items{}
for _, act := range getActions() {
itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
Return(act, nil).
Once()
}
setup := newSetup(&content.Content{
Collections: collsMock,
Clients: clMock,
Roles: rMock,
Items: itmMock,
}, t)
err := setup.Check(context.Background())
require.NoError(t, err)
require.False(t, setup.HasErrors())
rMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
clMock.AssertExpectations(t)
itmMock.AssertExpectations(t)
})
t.Run("Can't get role, storage returns error", func(t *testing.T) {
rMock := &rolesMock.Roles{}
rMock.On("Get", mock.Anything, spaceID, mock.Anything).
Return(nil, errors.New("can't get role")).
Once()
setup := newSetup(&content.Content{
Roles: rMock,
}, t)
err := setup.Check(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "role check error")
rMock.AssertExpectations(t)
})
t.Run("Can't get client, storage returns error", func(t *testing.T) {
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
clMock.On("Get", mock.Anything, spaceID, mock.Anything).
Return(nil, errors.New("can't get client")).
Once()
setup := newSetup(&content.Content{
Roles: rMock,
Clients: clMock,
}, t)
err := setup.Check(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "clients check error")
rMock.AssertExpectations(t)
clMock.AssertExpectations(t)
})
t.Run("Can't get collection, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
collsMock.On("Get", mock.Anything, spaceID, envID, mock.Anything).
Return(nil, errors.New("can't get collection")).
Once()
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(client, nil).
Once()
}
setup := newSetup(&content.Content{
Roles: rMock,
Clients: clMock,
Collections: collsMock,
}, t)
err := setup.Check(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "collections check error")
rMock.AssertExpectations(t)
clMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
})
t.Run("Can't get action, storage returns error", func(t *testing.T) {
collsMock := &collectionMock.Collections{}
for _, collection := range getCollections() {
collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
Return(collection, nil).
Once()
}
rMock := &rolesMock.Roles{}
for _, role := range getRoles() {
rMock.On("Get", mock.Anything, spaceID, role.ID).
Return(role, nil).
Once()
}
clMock := &clientsMock.Clients{}
for _, client := range getClients() {
clMock.On("Get", mock.Anything, spaceID, client.ID).
Return(client, nil).
Once()
}
itmMock := &itemsMock.Items{}
itmMock.On(
"Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, mock.Anything,
).
Return(nil, errors.New("can't find item")).
Once()
setup := newSetup(&content.Content{
Roles: rMock,
Clients: clMock,
Collections: collsMock,
Items: itmMock,
}, t)
err := setup.Check(context.Background())
require.Error(t, err)
assert.ErrorContains(t, err, "items check error")
rMock.AssertExpectations(t)
clMock.AssertExpectations(t)
collsMock.AssertExpectations(t)
})
//t.Run("Can't get task config, storage returns error", func(t *testing.T) {
// collsMock := &collectionMock.Collections{}
// for _, collection := range getCollections() {
// collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
// Return(collection, nil).
// Once()
// }
//
// rMock := &rolesMock.Roles{}
// for _, role := range getRoles() {
// rMock.On("Get", mock.Anything, spaceID, role.ID).
// Return(role, nil).
// Once()
// }
//
// clMock := &clientsMock.Clients{}
// for _, client := range getClients() {
// clMock.On("Get", mock.Anything, spaceID, client.ID).
// Return(client, nil).
// Once()
// }
//
// itmMock := &itemsMock.Items{}
// for _, act := range getActions() {
// itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
// Return(act, nil).
// Once()
// }
//
// itmMock.On(
// "Find", mock.Anything, spaceID, envID, tasks.TaskConfigCollection,
// mock.Anything,
// ).
// Return(nil, 0, errors.New("can't get task configs")).
// Once()
//
// setup := newSetup(&content.Content{
// Roles: rMock,
// Clients: clMock,
// Collections: collsMock,
// Items: itmMock,
// }, t)
//
// err := setup.Check(context.Background())
//
// require.Error(t, err)
// assert.ErrorContains(t, err, "task configs check error")
//
// rMock.AssertExpectations(t)
// clMock.AssertExpectations(t)
// collsMock.AssertExpectations(t)
//})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment