diff --git a/api/database/boltdb/tx.go b/api/database/boltdb/tx.go index b27add6b9..700d9db49 100644 --- a/api/database/boltdb/tx.go +++ b/api/database/boltdb/tx.go @@ -49,7 +49,7 @@ func (tx *DbTransaction) DeleteObject(bucketName string, key []byte) error { return bucket.Delete(key) } -func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, matching func(o interface{}) (id int, ok bool)) error { +func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, matchingFn func(o interface{}) (id int, ok bool)) error { bucket := tx.tx.Bucket([]byte(bucketName)) cursor := bucket.Cursor() @@ -59,7 +59,7 @@ func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, ma return err } - if id, ok := matching(obj); ok { + if id, ok := matchingFn(obj); ok { err := bucket.Delete(tx.conn.ConvertToKey(id)) if err != nil { return err @@ -115,45 +115,33 @@ func (tx *DbTransaction) CreateObjectWithStringId(bucketName string, id []byte, return bucket.Put(id, data) } -func (tx *DbTransaction) GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error { +func (tx *DbTransaction) GetAll(bucketName string, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error { bucket := tx.tx.Bucket([]byte(bucketName)) - cursor := bucket.Cursor() - for k, v := cursor.First(); k != nil; k, v = cursor.Next() { + return bucket.ForEach(func(k []byte, v []byte) error { err := tx.conn.UnmarshalObject(v, obj) - if err != nil { - return err + if err == nil { + obj, err = appendFn(obj) } - obj, err = append(obj) - if err != nil { - return err - } - } - - return nil + return err + }) } -func (tx *DbTransaction) GetAllWithJsoniter(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error { +func (tx *DbTransaction) GetAllWithJsoniter(bucketName string, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error { bucket := tx.tx.Bucket([]byte(bucketName)) - cursor := bucket.Cursor() - for k, v := cursor.First(); k != nil; k, v = cursor.Next() { + return bucket.ForEach(func(k []byte, v []byte) error { err := tx.conn.UnmarshalObjectWithJsoniter(v, obj) - if err != nil { - return err + if err == nil { + obj, err = appendFn(obj) } - obj, err = append(obj) - if err != nil { - return err - } - } - - return nil + return err + }) } -func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj interface{}, append func(o interface{}) (interface{}, error)) error { +func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error { cursor := tx.tx.Bucket([]byte(bucketName)).Cursor() for k, v := cursor.Seek(keyPrefix); k != nil && bytes.HasPrefix(k, keyPrefix); k, v = cursor.Next() { @@ -162,7 +150,7 @@ func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte return err } - obj, err = append(obj) + obj, err = appendFn(obj) if err != nil { return err } diff --git a/api/dataservices/customtemplate/customtemplate.go b/api/dataservices/customtemplate/customtemplate.go index 15b2b9aa3..3065b636a 100644 --- a/api/dataservices/customtemplate/customtemplate.go +++ b/api/dataservices/customtemplate/customtemplate.go @@ -1,17 +1,12 @@ package customtemplate import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "customtemplates" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "customtemplates" // Service represents a service for managing custom template data. type Service struct { @@ -38,22 +33,11 @@ func NewService(connection portainer.Connection) (*Service, error) { func (service *Service) CustomTemplates() ([]portainer.CustomTemplate, error) { var customTemplates = make([]portainer.CustomTemplate, 0) - err := service.connection.GetAll( + return customTemplates, service.connection.GetAll( BucketName, &portainer.CustomTemplate{}, - func(obj interface{}) (interface{}, error) { - //var tag portainer.Tag - customTemplate, ok := obj.(*portainer.CustomTemplate) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to CustomTemplate object") - return nil, fmt.Errorf("Failed to convert to CustomTemplate object: %s", obj) - } - customTemplates = append(customTemplates, *customTemplate) - - return &portainer.CustomTemplate{}, nil - }) - - return customTemplates, err + dataservices.AppendFn(&customTemplates), + ) } // CustomTemplate returns an custom template by ID. diff --git a/api/dataservices/edgegroup/tx.go b/api/dataservices/edgegroup/tx.go index f010af0c2..99f8d9da8 100644 --- a/api/dataservices/edgegroup/tx.go +++ b/api/dataservices/edgegroup/tx.go @@ -2,11 +2,10 @@ package edgegroup import ( "errors" - "fmt" + + "github.com/portainer/portainer/api/dataservices" portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" ) type ServiceTx struct { @@ -22,21 +21,11 @@ func (service ServiceTx) BucketName() string { func (service ServiceTx) EdgeGroups() ([]portainer.EdgeGroup, error) { var groups = make([]portainer.EdgeGroup, 0) - err := service.tx.GetAllWithJsoniter( + return groups, service.tx.GetAllWithJsoniter( BucketName, &portainer.EdgeGroup{}, - func(obj interface{}) (interface{}, error) { - group, ok := obj.(*portainer.EdgeGroup) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeGroup object") - return nil, fmt.Errorf("Failed to convert to EdgeGroup object: %s", obj) - } - groups = append(groups, *group) - - return &portainer.EdgeGroup{}, nil - }) - - return groups, err + dataservices.AppendFn(&groups), + ) } // EdgeGroup returns an Edge group by ID. diff --git a/api/dataservices/edgejob/edgejob.go b/api/dataservices/edgejob/edgejob.go index 98eb6d1c7..ba5e3cd74 100644 --- a/api/dataservices/edgejob/edgejob.go +++ b/api/dataservices/edgejob/edgejob.go @@ -1,11 +1,8 @@ package edgejob import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) // BucketName represents the name of the bucket where this service stores data. @@ -43,22 +40,11 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx { func (service *Service) EdgeJobs() ([]portainer.EdgeJob, error) { var edgeJobs = make([]portainer.EdgeJob, 0) - err := service.connection.GetAll( + return edgeJobs, service.connection.GetAll( BucketName, &portainer.EdgeJob{}, - func(obj interface{}) (interface{}, error) { - job, ok := obj.(*portainer.EdgeJob) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeJob object") - return nil, fmt.Errorf("Failed to convert to EdgeJob object: %s", obj) - } - - edgeJobs = append(edgeJobs, *job) - - return &portainer.EdgeJob{}, nil - }) - - return edgeJobs, err + dataservices.AppendFn(&edgeJobs), + ) } // EdgeJob returns an Edge job by ID diff --git a/api/dataservices/edgejob/tx.go b/api/dataservices/edgejob/tx.go index 91c704bd9..b70d363ca 100644 --- a/api/dataservices/edgejob/tx.go +++ b/api/dataservices/edgejob/tx.go @@ -2,11 +2,9 @@ package edgejob import ( "errors" - "fmt" portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) type ServiceTx struct { @@ -22,22 +20,11 @@ func (service ServiceTx) BucketName() string { func (service ServiceTx) EdgeJobs() ([]portainer.EdgeJob, error) { var edgeJobs = make([]portainer.EdgeJob, 0) - err := service.tx.GetAll( + return edgeJobs, service.tx.GetAll( BucketName, &portainer.EdgeJob{}, - func(obj interface{}) (interface{}, error) { - job, ok := obj.(*portainer.EdgeJob) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeJob object") - return nil, fmt.Errorf("failed to convert to EdgeJob object: %s", obj) - } - - edgeJobs = append(edgeJobs, *job) - - return &portainer.EdgeJob{}, nil - }) - - return edgeJobs, err + dataservices.AppendFn(&edgeJobs), + ) } // EdgeJob returns an Edge job by ID diff --git a/api/dataservices/edgestack/edgestack.go b/api/dataservices/edgestack/edgestack.go index d754ac025..b04a53d13 100644 --- a/api/dataservices/edgestack/edgestack.go +++ b/api/dataservices/edgestack/edgestack.go @@ -1,12 +1,10 @@ package edgestack import ( - "fmt" "sync" portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) // BucketName represents the name of the bucket where this service stores data. @@ -64,22 +62,11 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx { func (service *Service) EdgeStacks() ([]portainer.EdgeStack, error) { var stacks = make([]portainer.EdgeStack, 0) - err := service.connection.GetAll( + return stacks, service.connection.GetAll( BucketName, &portainer.EdgeStack{}, - func(obj interface{}) (interface{}, error) { - stack, ok := obj.(*portainer.EdgeStack) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeStack object") - return nil, fmt.Errorf("Failed to convert to EdgeStack object: %s", obj) - } - - stacks = append(stacks, *stack) - - return &portainer.EdgeStack{}, nil - }) - - return stacks, err + dataservices.AppendFn(&stacks), + ) } // EdgeStack returns an Edge stack by ID. diff --git a/api/dataservices/endpoint/tx.go b/api/dataservices/endpoint/tx.go index 0d742d4af..4b662ec48 100644 --- a/api/dataservices/endpoint/tx.go +++ b/api/dataservices/endpoint/tx.go @@ -1,9 +1,8 @@ package endpoint import ( - "fmt" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/internal/edge/cache" "github.com/rs/zerolog/log" @@ -80,22 +79,11 @@ func (service ServiceTx) DeleteEndpoint(ID portainer.EndpointID) error { func (service ServiceTx) Endpoints() ([]portainer.Endpoint, error) { var endpoints = make([]portainer.Endpoint, 0) - err := service.tx.GetAllWithJsoniter( + return endpoints, service.tx.GetAllWithJsoniter( BucketName, &portainer.Endpoint{}, - func(obj interface{}) (interface{}, error) { - endpoint, ok := obj.(*portainer.Endpoint) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Endpoint object") - return nil, fmt.Errorf("failed to convert to Endpoint object: %s", obj) - } - - endpoints = append(endpoints, *endpoint) - - return &portainer.Endpoint{}, nil - }) - - return endpoints, err + dataservices.AppendFn(&endpoints), + ) } func (service ServiceTx) EndpointIDByEdgeID(edgeID string) (portainer.EndpointID, bool) { diff --git a/api/dataservices/endpointgroup/endpointgroup.go b/api/dataservices/endpointgroup/endpointgroup.go index 4b62488ed..c205f93a4 100644 --- a/api/dataservices/endpointgroup/endpointgroup.go +++ b/api/dataservices/endpointgroup/endpointgroup.go @@ -1,11 +1,8 @@ package endpointgroup import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) const ( @@ -70,22 +67,11 @@ func (service *Service) DeleteEndpointGroup(ID portainer.EndpointGroupID) error func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) { var endpointGroups = make([]portainer.EndpointGroup, 0) - err := service.connection.GetAll( + return endpointGroups, service.connection.GetAll( BucketName, &portainer.EndpointGroup{}, - func(obj interface{}) (interface{}, error) { - endpointGroup, ok := obj.(*portainer.EndpointGroup) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointGroup object") - return nil, fmt.Errorf("Failed to convert to EndpointGroup object: %s", obj) - } - - endpointGroups = append(endpointGroups, *endpointGroup) - - return &portainer.EndpointGroup{}, nil - }) - - return endpointGroups, err + dataservices.AppendFn(&endpointGroups), + ) } // CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it. diff --git a/api/dataservices/endpointgroup/tx.go b/api/dataservices/endpointgroup/tx.go index b095c7646..4ea93c20b 100644 --- a/api/dataservices/endpointgroup/tx.go +++ b/api/dataservices/endpointgroup/tx.go @@ -1,11 +1,8 @@ package endpointgroup import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) type ServiceTx struct { @@ -46,22 +43,11 @@ func (service ServiceTx) DeleteEndpointGroup(ID portainer.EndpointGroupID) error func (service ServiceTx) EndpointGroups() ([]portainer.EndpointGroup, error) { var endpointGroups = make([]portainer.EndpointGroup, 0) - err := service.tx.GetAll( + return endpointGroups, service.tx.GetAll( BucketName, &portainer.EndpointGroup{}, - func(obj interface{}) (interface{}, error) { - endpointGroup, ok := obj.(*portainer.EndpointGroup) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointGroup object") - return nil, fmt.Errorf("failed to convert to EndpointGroup object: %s", obj) - } - - endpointGroups = append(endpointGroups, *endpointGroup) - - return &portainer.EndpointGroup{}, nil - }) - - return endpointGroups, err + dataservices.AppendFn(&endpointGroups), + ) } // CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it. diff --git a/api/dataservices/endpointrelation/endpointrelation.go b/api/dataservices/endpointrelation/endpointrelation.go index 99cc14414..4e65bceb7 100644 --- a/api/dataservices/endpointrelation/endpointrelation.go +++ b/api/dataservices/endpointrelation/endpointrelation.go @@ -1,9 +1,8 @@ package endpointrelation import ( - "fmt" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/internal/edge/cache" "github.com/rs/zerolog/log" @@ -54,22 +53,11 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx { func (service *Service) EndpointRelations() ([]portainer.EndpointRelation, error) { var all = make([]portainer.EndpointRelation, 0) - err := service.connection.GetAll( + return all, service.connection.GetAll( BucketName, &portainer.EndpointRelation{}, - func(obj interface{}) (interface{}, error) { - r, ok := obj.(*portainer.EndpointRelation) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointRelation object") - return nil, fmt.Errorf("Failed to convert to EndpointRelation object: %s", obj) - } - - all = append(all, *r) - - return &portainer.EndpointRelation{}, nil - }) - - return all, err + dataservices.AppendFn(&all), + ) } // EndpointRelation returns a Environment(Endpoint) relation object by EndpointID diff --git a/api/dataservices/endpointrelation/tx.go b/api/dataservices/endpointrelation/tx.go index 95e75f036..ae1733063 100644 --- a/api/dataservices/endpointrelation/tx.go +++ b/api/dataservices/endpointrelation/tx.go @@ -1,9 +1,8 @@ package endpointrelation import ( - "fmt" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/internal/edge/cache" "github.com/rs/zerolog/log" @@ -22,22 +21,11 @@ func (service ServiceTx) BucketName() string { func (service ServiceTx) EndpointRelations() ([]portainer.EndpointRelation, error) { var all = make([]portainer.EndpointRelation, 0) - err := service.tx.GetAll( + return all, service.tx.GetAll( BucketName, &portainer.EndpointRelation{}, - func(obj interface{}) (interface{}, error) { - r, ok := obj.(*portainer.EndpointRelation) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointRelation object") - return nil, fmt.Errorf("failed to convert to EndpointRelation object: %s", obj) - } - - all = append(all, *r) - - return &portainer.EndpointRelation{}, nil - }) - - return all, err + dataservices.AppendFn(&all), + ) } // EndpointRelation returns an Environment(Endpoint) relation object by EndpointID diff --git a/api/dataservices/extension/extension.go b/api/dataservices/extension/extension.go index 6cb4004d7..2af6fac9d 100644 --- a/api/dataservices/extension/extension.go +++ b/api/dataservices/extension/extension.go @@ -1,17 +1,12 @@ package extension import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "extension" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "extension" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -51,22 +46,12 @@ func (service *Service) Extension(ID portainer.ExtensionID) (*portainer.Extensio func (service *Service) Extensions() ([]portainer.Extension, error) { var extensions = make([]portainer.Extension, 0) - err := service.connection.GetAll( + return extensions, service.connection.GetAll( BucketName, &portainer.Extension{}, - func(obj interface{}) (interface{}, error) { - extension, ok := obj.(*portainer.Extension) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Extension object") - return nil, fmt.Errorf("Failed to convert to Extension object: %s", obj) - } + dataservices.AppendFn(&extensions), + ) - extensions = append(extensions, *extension) - - return &portainer.Extension{}, nil - }) - - return extensions, err } // Persist persists a extension inside the database. diff --git a/api/dataservices/fdoprofile/fdoprofile.go b/api/dataservices/fdoprofile/fdoprofile.go index 918546540..b91115885 100644 --- a/api/dataservices/fdoprofile/fdoprofile.go +++ b/api/dataservices/fdoprofile/fdoprofile.go @@ -1,11 +1,8 @@ package fdoprofile import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) const ( @@ -38,21 +35,11 @@ func NewService(connection portainer.Connection) (*Service, error) { func (service *Service) FDOProfiles() ([]portainer.FDOProfile, error) { var fdoProfiles = make([]portainer.FDOProfile, 0) - err := service.connection.GetAll( + return fdoProfiles, service.connection.GetAll( BucketName, &portainer.FDOProfile{}, - func(obj interface{}) (interface{}, error) { - fdoProfile, ok := obj.(*portainer.FDOProfile) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to FDOProfile object") - - return nil, fmt.Errorf("Failed to convert to FDOProfile object: %s", obj) - } - fdoProfiles = append(fdoProfiles, *fdoProfile) - return &portainer.FDOProfile{}, nil - }) - - return fdoProfiles, err + dataservices.AppendFn(&fdoProfiles), + ) } // FDOProfile returns an FDO Profile by ID. diff --git a/api/dataservices/helmuserrepository/helmuserrepository.go b/api/dataservices/helmuserrepository/helmuserrepository.go index 375b86151..327f30bfb 100644 --- a/api/dataservices/helmuserrepository/helmuserrepository.go +++ b/api/dataservices/helmuserrepository/helmuserrepository.go @@ -1,17 +1,12 @@ package helmuserrepository import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "helm_user_repository" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "helm_user_repository" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -38,46 +33,24 @@ func NewService(connection portainer.Connection) (*Service, error) { func (service *Service) HelmUserRepositories() ([]portainer.HelmUserRepository, error) { var repos = make([]portainer.HelmUserRepository, 0) - err := service.connection.GetAll( + return repos, service.connection.GetAll( BucketName, &portainer.HelmUserRepository{}, - func(obj interface{}) (interface{}, error) { - r, ok := obj.(*portainer.HelmUserRepository) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object") - return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj) - } - - repos = append(repos, *r) - - return &portainer.HelmUserRepository{}, nil - }) - - return repos, err + dataservices.AppendFn(&repos), + ) } // HelmUserRepositoryByUserID return an array containing all the HelmUserRepository objects where the specified userID is present. func (service *Service) HelmUserRepositoryByUserID(userID portainer.UserID) ([]portainer.HelmUserRepository, error) { var result = make([]portainer.HelmUserRepository, 0) - err := service.connection.GetAll( + return result, service.connection.GetAll( BucketName, &portainer.HelmUserRepository{}, - func(obj interface{}) (interface{}, error) { - record, ok := obj.(*portainer.HelmUserRepository) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object") - return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj) - } - - if record.UserID == userID { - result = append(result, *record) - } - - return &portainer.HelmUserRepository{}, nil - }) - - return result, err + dataservices.FilterFn(&result, func(e portainer.HelmUserRepository) bool { + return e.UserID == userID + }), + ) } // CreateHelmUserRepository creates a new HelmUserRepository object. diff --git a/api/dataservices/helpers.go b/api/dataservices/helpers.go new file mode 100644 index 000000000..34da6e319 --- /dev/null +++ b/api/dataservices/helpers.go @@ -0,0 +1,68 @@ +package dataservices + +import ( + "errors" + "fmt" + + perrors "github.com/portainer/portainer/api/dataservices/errors" + + "github.com/rs/zerolog/log" +) + +// ErrStop signals the stop of computation when filtering results +var ErrStop = errors.New("stop") + +func IsErrObjectNotFound(e error) bool { + return errors.Is(e, perrors.ErrObjectNotFound) +} + +// AppendFn appends elements to the given collection slice +func AppendFn[T any](collection *[]T) func(obj interface{}) (interface{}, error) { + return func(obj interface{}) (interface{}, error) { + element, ok := obj.(*T) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("type assertion failed") + return nil, fmt.Errorf("failed to convert to %T object: %#v", new(T), obj) + } + + *collection = append(*collection, *element) + + return new(T), nil + } +} + +// FilterFn appends elements to the given collection when the predicate is true +func FilterFn[T any](collection *[]T, predicate func(T) bool) func(obj interface{}) (interface{}, error) { + return func(obj interface{}) (interface{}, error) { + element, ok := obj.(*T) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("type assertion failed") + return nil, fmt.Errorf("failed to convert to %T object: %#v", new(T), obj) + } + + if predicate(*element) { + *collection = append(*collection, *element) + } + + return new(T), nil + } +} + +// FirstFn sets the element to the first one that satisfies the predicate and stops the computation, returns ErrStop on +// success +func FirstFn[T any](element *T, predicate func(T) bool) func(obj interface{}) (interface{}, error) { + return func(obj interface{}) (interface{}, error) { + e, ok := obj.(*T) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("type assertion failed") + return nil, fmt.Errorf("failed to convert to %T object: %#v", new(T), obj) + } + + if predicate(*e) { + *element = *e + return new(T), ErrStop + } + + return new(T), nil + } +} diff --git a/api/dataservices/interface.go b/api/dataservices/interface.go index cacd332ae..9b1300be6 100644 --- a/api/dataservices/interface.go +++ b/api/dataservices/interface.go @@ -1,13 +1,11 @@ package dataservices import ( - "errors" "io" "time" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/database/models" - dserrors "github.com/portainer/portainer/api/dataservices/errors" ) type ( @@ -322,7 +320,3 @@ type ( BucketName() string } ) - -func IsErrObjectNotFound(e error) bool { - return errors.Is(e, dserrors.ErrObjectNotFound) -} diff --git a/api/dataservices/registry/registry.go b/api/dataservices/registry/registry.go index cc11c72f6..1c615f1ec 100644 --- a/api/dataservices/registry/registry.go +++ b/api/dataservices/registry/registry.go @@ -1,11 +1,8 @@ package registry import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) // BucketName represents the name of the bucket where this service stores data. @@ -56,22 +53,11 @@ func (service *Service) Registry(ID portainer.RegistryID) (*portainer.Registry, func (service *Service) Registries() ([]portainer.Registry, error) { var registries = make([]portainer.Registry, 0) - err := service.connection.GetAll( + return registries, service.connection.GetAll( BucketName, &portainer.Registry{}, - func(obj interface{}) (interface{}, error) { - registry, ok := obj.(*portainer.Registry) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Registry object") - return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj) - } - - registries = append(registries, *registry) - - return &portainer.Registry{}, nil - }) - - return registries, err + dataservices.AppendFn(®istries), + ) } // Create creates a new registry. diff --git a/api/dataservices/registry/tx.go b/api/dataservices/registry/tx.go index ce1cc444c..fa17a674e 100644 --- a/api/dataservices/registry/tx.go +++ b/api/dataservices/registry/tx.go @@ -1,10 +1,8 @@ package registry import ( - "fmt" - portainer "github.com/portainer/portainer/api" - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) type ServiceTx struct { @@ -33,22 +31,11 @@ func (service ServiceTx) Registry(ID portainer.RegistryID) (*portainer.Registry, func (service ServiceTx) Registries() ([]portainer.Registry, error) { var registries = make([]portainer.Registry, 0) - err := service.tx.GetAll( + return registries, service.tx.GetAll( BucketName, &portainer.Registry{}, - func(obj interface{}) (interface{}, error) { - registry, ok := obj.(*portainer.Registry) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Registry object") - return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj) - } - - registries = append(registries, *registry) - - return &portainer.Registry{}, nil - }) - - return registries, err + dataservices.AppendFn(®istries), + ) } // Create creates a new registry. diff --git a/api/dataservices/resourcecontrol/resourcecontrol.go b/api/dataservices/resourcecontrol/resourcecontrol.go index 624f078ec..025202fcd 100644 --- a/api/dataservices/resourcecontrol/resourcecontrol.go +++ b/api/dataservices/resourcecontrol/resourcecontrol.go @@ -5,6 +5,7 @@ import ( "fmt" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/rs/zerolog/log" ) @@ -94,22 +95,11 @@ func (service *Service) ResourceControlByResourceIDAndType(resourceID string, re func (service *Service) ResourceControls() ([]portainer.ResourceControl, error) { var rcs = make([]portainer.ResourceControl, 0) - err := service.connection.GetAll( + return rcs, service.connection.GetAll( BucketName, &portainer.ResourceControl{}, - func(obj interface{}) (interface{}, error) { - rc, ok := obj.(*portainer.ResourceControl) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object") - return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj) - } - - rcs = append(rcs, *rc) - - return &portainer.ResourceControl{}, nil - }) - - return rcs, err + dataservices.AppendFn(&rcs), + ) } // CreateResourceControl creates a new ResourceControl object diff --git a/api/dataservices/resourcecontrol/tx.go b/api/dataservices/resourcecontrol/tx.go index b18736571..b188a3919 100644 --- a/api/dataservices/resourcecontrol/tx.go +++ b/api/dataservices/resourcecontrol/tx.go @@ -5,6 +5,7 @@ import ( "fmt" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/rs/zerolog/log" ) @@ -72,22 +73,11 @@ func (service ServiceTx) ResourceControlByResourceIDAndType(resourceID string, r func (service ServiceTx) ResourceControls() ([]portainer.ResourceControl, error) { var rcs = make([]portainer.ResourceControl, 0) - err := service.tx.GetAll( + return rcs, service.tx.GetAll( BucketName, &portainer.ResourceControl{}, - func(obj interface{}) (interface{}, error) { - rc, ok := obj.(*portainer.ResourceControl) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object") - return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj) - } - - rcs = append(rcs, *rc) - - return &portainer.ResourceControl{}, nil - }) - - return rcs, err + dataservices.AppendFn(&rcs), + ) } // CreateResourceControl creates a new ResourceControl object diff --git a/api/dataservices/role/role.go b/api/dataservices/role/role.go index 617cefa0d..f0c8c54f0 100644 --- a/api/dataservices/role/role.go +++ b/api/dataservices/role/role.go @@ -1,11 +1,8 @@ package role import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) // BucketName represents the name of the bucket where this service stores data. @@ -56,22 +53,11 @@ func (service *Service) Role(ID portainer.RoleID) (*portainer.Role, error) { func (service *Service) Roles() ([]portainer.Role, error) { var sets = make([]portainer.Role, 0) - err := service.connection.GetAll( + return sets, service.connection.GetAll( BucketName, &portainer.Role{}, - func(obj interface{}) (interface{}, error) { - set, ok := obj.(*portainer.Role) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Role object") - return nil, fmt.Errorf("failed to convert to Role object: %s", obj) - } - - sets = append(sets, *set) - - return &portainer.Role{}, nil - }) - - return sets, err + dataservices.AppendFn(&sets), + ) } // CreateRole creates a new Role. diff --git a/api/dataservices/role/tx.go b/api/dataservices/role/tx.go index 5e35c65c8..a756b4957 100644 --- a/api/dataservices/role/tx.go +++ b/api/dataservices/role/tx.go @@ -1,11 +1,8 @@ package role import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) // Service represents a service for managing environment(endpoint) data. @@ -35,22 +32,11 @@ func (service ServiceTx) Role(ID portainer.RoleID) (*portainer.Role, error) { func (service ServiceTx) Roles() ([]portainer.Role, error) { var sets = make([]portainer.Role, 0) - err := service.tx.GetAll( + return sets, service.tx.GetAll( BucketName, &portainer.Role{}, - func(obj interface{}) (interface{}, error) { - set, ok := obj.(*portainer.Role) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Role object") - return nil, fmt.Errorf("failed to convert to Role object: %s", obj) - } - - sets = append(sets, *set) - - return &portainer.Role{}, nil - }) - - return sets, err + dataservices.AppendFn(&sets), + ) } // CreateRole creates a new Role. diff --git a/api/dataservices/schedule/schedule.go b/api/dataservices/schedule/schedule.go index f338938b2..d8df531c3 100644 --- a/api/dataservices/schedule/schedule.go +++ b/api/dataservices/schedule/schedule.go @@ -1,17 +1,12 @@ package schedule import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "schedules" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "schedules" // Service represents a service for managing schedule data. type Service struct { @@ -63,22 +58,11 @@ func (service *Service) DeleteSchedule(ID portainer.ScheduleID) error { func (service *Service) Schedules() ([]portainer.Schedule, error) { var schedules = make([]portainer.Schedule, 0) - err := service.connection.GetAll( + return schedules, service.connection.GetAll( BucketName, &portainer.Schedule{}, - func(obj interface{}) (interface{}, error) { - schedule, ok := obj.(*portainer.Schedule) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Schedule object") - return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj) - } - - schedules = append(schedules, *schedule) - - return &portainer.Schedule{}, nil - }) - - return schedules, err + dataservices.AppendFn(&schedules), + ) } // SchedulesByJobType return a array containing all the schedules @@ -86,24 +70,13 @@ func (service *Service) Schedules() ([]portainer.Schedule, error) { func (service *Service) SchedulesByJobType(jobType portainer.JobType) ([]portainer.Schedule, error) { var schedules = make([]portainer.Schedule, 0) - err := service.connection.GetAll( + return schedules, service.connection.GetAll( BucketName, &portainer.Schedule{}, - func(obj interface{}) (interface{}, error) { - schedule, ok := obj.(*portainer.Schedule) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Schedule object") - return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj) - } - - if schedule.JobType == jobType { - schedules = append(schedules, *schedule) - } - - return &portainer.Schedule{}, nil - }) - - return schedules, err + dataservices.FilterFn(&schedules, func(e portainer.Schedule) bool { + return e.JobType == jobType + }), + ) } // Create assign an ID to a new schedule and saves it. diff --git a/api/dataservices/snapshot/snapshot.go b/api/dataservices/snapshot/snapshot.go index 489fd68dd..369fe17a8 100644 --- a/api/dataservices/snapshot/snapshot.go +++ b/api/dataservices/snapshot/snapshot.go @@ -1,11 +1,8 @@ package snapshot import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) const ( @@ -53,20 +50,11 @@ func (service *Service) Snapshot(endpointID portainer.EndpointID) (*portainer.Sn func (service *Service) Snapshots() ([]portainer.Snapshot, error) { var snapshots = make([]portainer.Snapshot, 0) - err := service.connection.GetAllWithJsoniter( + return snapshots, service.connection.GetAllWithJsoniter( BucketName, &portainer.Snapshot{}, - func(obj interface{}) (interface{}, error) { - snapshot, ok := obj.(*portainer.Snapshot) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Snapshot object") - return nil, fmt.Errorf("failed to convert to Snapshot object: %s", obj) - } - snapshots = append(snapshots, *snapshot) - return &portainer.Snapshot{}, nil - }) - - return snapshots, err + dataservices.AppendFn(&snapshots), + ) } func (service *Service) UpdateSnapshot(snapshot *portainer.Snapshot) error { diff --git a/api/dataservices/snapshot/tx.go b/api/dataservices/snapshot/tx.go index 52f54aabd..b39b69ca2 100644 --- a/api/dataservices/snapshot/tx.go +++ b/api/dataservices/snapshot/tx.go @@ -1,11 +1,8 @@ package snapshot import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) type ServiceTx struct { @@ -32,20 +29,11 @@ func (service ServiceTx) Snapshot(endpointID portainer.EndpointID) (*portainer.S func (service ServiceTx) Snapshots() ([]portainer.Snapshot, error) { var snapshots = make([]portainer.Snapshot, 0) - err := service.tx.GetAllWithJsoniter( + return snapshots, service.tx.GetAllWithJsoniter( BucketName, &portainer.Snapshot{}, - func(obj interface{}) (interface{}, error) { - snapshot, ok := obj.(*portainer.Snapshot) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Snapshot object") - return nil, fmt.Errorf("failed to convert to Snapshot object: %s", obj) - } - snapshots = append(snapshots, *snapshot) - return &portainer.Snapshot{}, nil - }) - - return snapshots, err + dataservices.AppendFn(&snapshots), + ) } func (service ServiceTx) UpdateSnapshot(snapshot *portainer.Snapshot) error { diff --git a/api/dataservices/stack/stack.go b/api/dataservices/stack/stack.go index b979ded3f..cef1fdeba 100644 --- a/api/dataservices/stack/stack.go +++ b/api/dataservices/stack/stack.go @@ -2,19 +2,15 @@ package stack import ( "errors" - "fmt" "strings" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" dserrors "github.com/portainer/portainer/api/dataservices/errors" - - "github.com/rs/zerolog/log" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "stacks" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "stacks" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -52,29 +48,20 @@ func (service *Service) Stack(ID portainer.StackID) (*portainer.Stack, error) { // StackByName returns a stack object by name. func (service *Service) StackByName(name string) (*portainer.Stack, error) { - var s *portainer.Stack + var s portainer.Stack - stop := fmt.Errorf("ok") err := service.connection.GetAll( BucketName, &portainer.Stack{}, - func(obj interface{}) (interface{}, error) { - stack, ok := obj.(*portainer.Stack) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object") - return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj) - } + dataservices.FirstFn(&s, func(e portainer.Stack) bool { + return e.Name == name + }), + ) - if stack.Name == name { - s = stack - return nil, stop - } - - return &portainer.Stack{}, nil - }) - if errors.Is(err, stop) { - return s, nil + if errors.Is(err, dataservices.ErrStop) { + return &s, nil } + if err == nil { return nil, dserrors.ErrObjectNotFound } @@ -86,46 +73,24 @@ func (service *Service) StackByName(name string) (*portainer.Stack, error) { func (service *Service) StacksByName(name string) ([]portainer.Stack, error) { var stacks = make([]portainer.Stack, 0) - err := service.connection.GetAll( + return stacks, service.connection.GetAll( BucketName, &portainer.Stack{}, - func(obj interface{}) (interface{}, error) { - stack, ok := obj.(portainer.Stack) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object") - return nil, fmt.Errorf("failed to convert to Stack object: %s", obj) - } - - if stack.Name == name { - stacks = append(stacks, stack) - } - - return &portainer.Stack{}, nil - }) - - return stacks, err + dataservices.FilterFn(&stacks, func(e portainer.Stack) bool { + return e.Name == name + }), + ) } // Stacks returns an array containing all the stacks. func (service *Service) Stacks() ([]portainer.Stack, error) { var stacks = make([]portainer.Stack, 0) - err := service.connection.GetAll( + return stacks, service.connection.GetAll( BucketName, &portainer.Stack{}, - func(obj interface{}) (interface{}, error) { - stack, ok := obj.(*portainer.Stack) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object") - return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj) - } - - stacks = append(stacks, *stack) - - return &portainer.Stack{}, nil - }) - - return stacks, err + dataservices.AppendFn(&stacks), + ) } // GetNextIdentifier returns the next identifier for a stack. @@ -153,30 +118,20 @@ func (service *Service) DeleteStack(ID portainer.StackID) error { // StackByWebhookID returns a pointer to a stack object by webhook ID. // It returns nil, errors.ErrObjectNotFound if there's no stack associated with the webhook ID. func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) { - var s *portainer.Stack - stop := fmt.Errorf("ok") + var s portainer.Stack + err := service.connection.GetAll( BucketName, &portainer.Stack{}, - func(obj interface{}) (interface{}, error) { - var ok bool - s, ok = obj.(*portainer.Stack) + dataservices.FirstFn(&s, func(e portainer.Stack) bool { + return e.AutoUpdate != nil && strings.EqualFold(e.AutoUpdate.Webhook, id) + }), + ) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object") - - return &portainer.Stack{}, nil - } - - if s.AutoUpdate != nil && strings.EqualFold(s.AutoUpdate.Webhook, id) { - return nil, stop - } - - return &portainer.Stack{}, nil - }) - if errors.Is(err, stop) { - return s, nil + if errors.Is(err, dataservices.ErrStop) { + return &s, nil } + if err == nil { return nil, dserrors.ErrObjectNotFound } @@ -189,22 +144,11 @@ func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) { func (service *Service) RefreshableStacks() ([]portainer.Stack, error) { stacks := make([]portainer.Stack, 0) - err := service.connection.GetAll( + return stacks, service.connection.GetAll( BucketName, &portainer.Stack{}, - func(obj interface{}) (interface{}, error) { - stack, ok := obj.(*portainer.Stack) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object") - return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj) - } - - if stack.AutoUpdate != nil && stack.AutoUpdate.Interval != "" { - stacks = append(stacks, *stack) - } - - return &portainer.Stack{}, nil - }) - - return stacks, err + dataservices.FilterFn(&stacks, func(e portainer.Stack) bool { + return e.AutoUpdate != nil && e.AutoUpdate.Interval != "" + }), + ) } diff --git a/api/dataservices/tag/tag.go b/api/dataservices/tag/tag.go index b831a3f0b..d71700e87 100644 --- a/api/dataservices/tag/tag.go +++ b/api/dataservices/tag/tag.go @@ -1,11 +1,8 @@ package tag import ( - "fmt" - portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) const ( @@ -45,22 +42,11 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx { func (service *Service) Tags() ([]portainer.Tag, error) { var tags = make([]portainer.Tag, 0) - err := service.connection.GetAll( + return tags, service.connection.GetAll( BucketName, &portainer.Tag{}, - func(obj interface{}) (interface{}, error) { - tag, ok := obj.(*portainer.Tag) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Tag object") - return nil, fmt.Errorf("Failed to convert to Tag object: %s", obj) - } - - tags = append(tags, *tag) - - return &portainer.Tag{}, nil - }) - - return tags, err + dataservices.AppendFn(&tags), + ) } // Tag returns a tag by ID. diff --git a/api/dataservices/tag/tx.go b/api/dataservices/tag/tx.go index 0686c05ea..642f8f917 100644 --- a/api/dataservices/tag/tx.go +++ b/api/dataservices/tag/tx.go @@ -2,11 +2,9 @@ package tag import ( "errors" - "fmt" portainer "github.com/portainer/portainer/api" - - "github.com/rs/zerolog/log" + "github.com/portainer/portainer/api/dataservices" ) type ServiceTx struct { @@ -22,22 +20,11 @@ func (service ServiceTx) BucketName() string { func (service ServiceTx) Tags() ([]portainer.Tag, error) { var tags = make([]portainer.Tag, 0) - err := service.tx.GetAll( + return tags, service.tx.GetAll( BucketName, &portainer.Tag{}, - func(obj interface{}) (interface{}, error) { - tag, ok := obj.(*portainer.Tag) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Tag object") - return nil, fmt.Errorf("failed to convert to Tag object: %s", obj) - } - - tags = append(tags, *tag) - - return &portainer.Tag{}, nil - }) - - return tags, err + dataservices.AppendFn(&tags), + ) } // Tag returns a tag by ID. diff --git a/api/dataservices/team/team.go b/api/dataservices/team/team.go index d53fa6ccb..570532aed 100644 --- a/api/dataservices/team/team.go +++ b/api/dataservices/team/team.go @@ -2,19 +2,15 @@ package team import ( "errors" - "fmt" "strings" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" dserrors "github.com/portainer/portainer/api/dataservices/errors" - - "github.com/rs/zerolog/log" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "teams" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "teams" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -52,29 +48,20 @@ func (service *Service) Team(ID portainer.TeamID) (*portainer.Team, error) { // TeamByName returns a team by name. func (service *Service) TeamByName(name string) (*portainer.Team, error) { - var t *portainer.Team + var t portainer.Team - stop := fmt.Errorf("ok") err := service.connection.GetAll( BucketName, &portainer.Team{}, - func(obj interface{}) (interface{}, error) { - team, ok := obj.(*portainer.Team) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Team object") - return nil, fmt.Errorf("Failed to convert to Team object: %s", obj) - } + dataservices.FirstFn(&t, func(e portainer.Team) bool { + return strings.EqualFold(e.Name, name) + }), + ) - if strings.EqualFold(team.Name, name) { - t = team - return nil, stop - } - - return &portainer.Team{}, nil - }) - if errors.Is(err, stop) { - return t, nil + if errors.Is(err, dataservices.ErrStop) { + return &t, nil } + if err == nil { return nil, dserrors.ErrObjectNotFound } @@ -86,22 +73,11 @@ func (service *Service) TeamByName(name string) (*portainer.Team, error) { func (service *Service) Teams() ([]portainer.Team, error) { var teams = make([]portainer.Team, 0) - err := service.connection.GetAll( + return teams, service.connection.GetAll( BucketName, &portainer.Team{}, - func(obj interface{}) (interface{}, error) { - team, ok := obj.(*portainer.Team) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Team object") - return nil, fmt.Errorf("Failed to convert to Team object: %s", obj) - } - - teams = append(teams, *team) - - return &portainer.Team{}, nil - }) - - return teams, err + dataservices.AppendFn(&teams), + ) } // UpdateTeam saves a Team. diff --git a/api/dataservices/teammembership/teammembership.go b/api/dataservices/teammembership/teammembership.go index cfea717fd..1c2a8d224 100644 --- a/api/dataservices/teammembership/teammembership.go +++ b/api/dataservices/teammembership/teammembership.go @@ -4,6 +4,7 @@ import ( "fmt" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/rs/zerolog/log" ) @@ -56,70 +57,37 @@ func (service *Service) TeamMembership(ID portainer.TeamMembershipID) (*portaine func (service *Service) TeamMemberships() ([]portainer.TeamMembership, error) { var memberships = make([]portainer.TeamMembership, 0) - err := service.connection.GetAll( + return memberships, service.connection.GetAll( BucketName, &portainer.TeamMembership{}, - func(obj interface{}) (interface{}, error) { - membership, ok := obj.(*portainer.TeamMembership) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") - return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) - } - - memberships = append(memberships, *membership) - - return &portainer.TeamMembership{}, nil - }) - - return memberships, err + dataservices.AppendFn(&memberships), + ) } // TeamMembershipsByUserID return an array containing all the TeamMembership objects where the specified userID is present. func (service *Service) TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error) { var memberships = make([]portainer.TeamMembership, 0) - err := service.connection.GetAll( + return memberships, service.connection.GetAll( BucketName, &portainer.TeamMembership{}, - func(obj interface{}) (interface{}, error) { - membership, ok := obj.(*portainer.TeamMembership) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") - return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) - } - - if membership.UserID == userID { - memberships = append(memberships, *membership) - } - - return &portainer.TeamMembership{}, nil - }) - - return memberships, err + dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool { + return e.UserID == userID + }), + ) } // TeamMembershipsByTeamID return an array containing all the TeamMembership objects where the specified teamID is present. func (service *Service) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error) { var memberships = make([]portainer.TeamMembership, 0) - err := service.connection.GetAll( + return memberships, service.connection.GetAll( BucketName, &portainer.TeamMembership{}, - func(obj interface{}) (interface{}, error) { - membership, ok := obj.(*portainer.TeamMembership) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") - return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) - } - - if membership.TeamID == teamID { - memberships = append(memberships, *membership) - } - - return &portainer.TeamMembership{}, nil - }) - - return memberships, err + dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool { + return e.TeamID == teamID + }), + ) } // UpdateTeamMembership saves a TeamMembership object. diff --git a/api/dataservices/teammembership/tx.go b/api/dataservices/teammembership/tx.go index b7fcefeb5..3216e8430 100644 --- a/api/dataservices/teammembership/tx.go +++ b/api/dataservices/teammembership/tx.go @@ -4,6 +4,7 @@ import ( "fmt" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" "github.com/rs/zerolog/log" ) @@ -34,70 +35,37 @@ func (service ServiceTx) TeamMembership(ID portainer.TeamMembershipID) (*portain func (service ServiceTx) TeamMemberships() ([]portainer.TeamMembership, error) { var memberships = make([]portainer.TeamMembership, 0) - err := service.tx.GetAll( + return memberships, service.tx.GetAll( BucketName, &portainer.TeamMembership{}, - func(obj interface{}) (interface{}, error) { - membership, ok := obj.(*portainer.TeamMembership) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") - return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) - } - - memberships = append(memberships, *membership) - - return &portainer.TeamMembership{}, nil - }) - - return memberships, err + dataservices.AppendFn(&memberships), + ) } // TeamMembershipsByUserID return an array containing all the TeamMembership objects where the specified userID is present. func (service ServiceTx) TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error) { var memberships = make([]portainer.TeamMembership, 0) - err := service.tx.GetAll( + return memberships, service.tx.GetAll( BucketName, &portainer.TeamMembership{}, - func(obj interface{}) (interface{}, error) { - membership, ok := obj.(*portainer.TeamMembership) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") - return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) - } - - if membership.UserID == userID { - memberships = append(memberships, *membership) - } - - return &portainer.TeamMembership{}, nil - }) - - return memberships, err + dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool { + return e.UserID == userID + }), + ) } // TeamMembershipsByTeamID return an array containing all the TeamMembership objects where the specified teamID is present. func (service ServiceTx) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error) { var memberships = make([]portainer.TeamMembership, 0) - err := service.tx.GetAll( + return memberships, service.tx.GetAll( BucketName, &portainer.TeamMembership{}, - func(obj interface{}) (interface{}, error) { - membership, ok := obj.(*portainer.TeamMembership) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") - return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) - } - - if membership.TeamID == teamID { - memberships = append(memberships, *membership) - } - - return &portainer.TeamMembership{}, nil - }) - - return memberships, err + dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool { + return e.TeamID == teamID + }), + ) } // UpdateTeamMembership saves a TeamMembership object. diff --git a/api/dataservices/user/user.go b/api/dataservices/user/user.go index 1189f55b9..7b655ece1 100644 --- a/api/dataservices/user/user.go +++ b/api/dataservices/user/user.go @@ -2,19 +2,15 @@ package user import ( "errors" - "fmt" "strings" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" dserrors "github.com/portainer/portainer/api/dataservices/errors" - - "github.com/rs/zerolog/log" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "users" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "users" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -52,29 +48,18 @@ func (service *Service) User(ID portainer.UserID) (*portainer.User, error) { // UserByUsername returns a user by username. func (service *Service) UserByUsername(username string) (*portainer.User, error) { - var u *portainer.User - stop := fmt.Errorf("ok") + var u portainer.User + err := service.connection.GetAll( BucketName, &portainer.User{}, - func(obj interface{}) (interface{}, error) { - user, ok := obj.(*portainer.User) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object") + dataservices.FirstFn(&u, func(e portainer.User) bool { + return strings.EqualFold(e.Username, username) + }), + ) - return nil, fmt.Errorf("Failed to convert to User object: %s", obj) - } - - if strings.EqualFold(user.Username, username) { - u = user - return nil, stop - } - - return &portainer.User{}, nil - }) - - if errors.Is(err, stop) { - return u, nil + if errors.Is(err, dataservices.ErrStop) { + return &u, nil } if err == nil { @@ -88,48 +73,24 @@ func (service *Service) UserByUsername(username string) (*portainer.User, error) func (service *Service) Users() ([]portainer.User, error) { var users = make([]portainer.User, 0) - err := service.connection.GetAll( + return users, service.connection.GetAll( BucketName, &portainer.User{}, - func(obj interface{}) (interface{}, error) { - user, ok := obj.(*portainer.User) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object") - - return nil, fmt.Errorf("Failed to convert to User object: %s", obj) - } - - users = append(users, *user) - - return &portainer.User{}, nil - }) - - return users, err + dataservices.AppendFn(&users), + ) } // UsersByRole return an array containing all the users with the specified role. func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User, error) { var users = make([]portainer.User, 0) - err := service.connection.GetAll( + return users, service.connection.GetAll( BucketName, &portainer.User{}, - func(obj interface{}) (interface{}, error) { - user, ok := obj.(*portainer.User) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object") - - return nil, fmt.Errorf("Failed to convert to User object: %s", obj) - } - - if user.Role == role { - users = append(users, *user) - } - - return &portainer.User{}, nil - }) - - return users, err + dataservices.FilterFn(&users, func(e portainer.User) bool { + return e.Role == role + }), + ) } // UpdateUser saves a user. diff --git a/api/dataservices/webhook/webhook.go b/api/dataservices/webhook/webhook.go index 8dd40dafb..7b102d532 100644 --- a/api/dataservices/webhook/webhook.go +++ b/api/dataservices/webhook/webhook.go @@ -2,18 +2,14 @@ package webhook import ( "errors" - "fmt" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" dserrors "github.com/portainer/portainer/api/dataservices/errors" - - "github.com/rs/zerolog/log" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "webhooks" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "webhooks" // Service represents a service for managing webhook data. type Service struct { @@ -40,22 +36,11 @@ func NewService(connection portainer.Connection) (*Service, error) { func (service *Service) Webhooks() ([]portainer.Webhook, error) { var webhooks = make([]portainer.Webhook, 0) - err := service.connection.GetAll( + return webhooks, service.connection.GetAll( BucketName, &portainer.Webhook{}, - func(obj interface{}) (interface{}, error) { - webhook, ok := obj.(*portainer.Webhook) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object") - return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj) - } - - webhooks = append(webhooks, *webhook) - - return &portainer.Webhook{}, nil - }) - - return webhooks, err + dataservices.AppendFn(&webhooks), + ) } // Webhook returns a webhook by ID. @@ -73,29 +58,18 @@ func (service *Service) Webhook(ID portainer.WebhookID) (*portainer.Webhook, err // WebhookByResourceID returns a webhook by the ResourceID it is associated with. func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, error) { - var w *portainer.Webhook - stop := fmt.Errorf("ok") + var w portainer.Webhook + err := service.connection.GetAll( BucketName, &portainer.Webhook{}, - func(obj interface{}) (interface{}, error) { - webhook, ok := obj.(*portainer.Webhook) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object") + dataservices.FirstFn(&w, func(e portainer.Webhook) bool { + return e.ResourceID == ID + }), + ) - return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj) - } - - if webhook.ResourceID == ID { - w = webhook - return nil, stop - } - - return &portainer.Webhook{}, nil - }) - - if errors.Is(err, stop) { - return w, nil + if errors.Is(err, dataservices.ErrStop) { + return &w, nil } if err == nil { @@ -107,29 +81,18 @@ func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, erro // WebhookByToken returns a webhook by the random token it is associated with. func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error) { - var w *portainer.Webhook - stop := fmt.Errorf("ok") + var w portainer.Webhook + err := service.connection.GetAll( BucketName, &portainer.Webhook{}, - func(obj interface{}) (interface{}, error) { - webhook, ok := obj.(*portainer.Webhook) - if !ok { - log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object") + dataservices.FirstFn(&w, func(e portainer.Webhook) bool { + return e.Token == token + }), + ) - return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj) - } - - if webhook.Token == token { - w = webhook - return nil, stop - } - - return &portainer.Webhook{}, nil - }) - - if errors.Is(err, stop) { - return w, nil + if errors.Is(err, dataservices.ErrStop) { + return &w, nil } if err == nil {