From 706d66a76e8b3733629a8c210ba8d9969f24710d Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Thu, 4 May 2023 11:51:30 -0300 Subject: [PATCH] feat(teammemberships): add support for transactions EE-5412 (#8900) --- .../teammembership/teammembership.go | 13 +- api/dataservices/teammembership/tx.go | 186 ++++++++++++++++++ api/datastore/services_tx.go | 15 +- 3 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 api/dataservices/teammembership/tx.go diff --git a/api/dataservices/teammembership/teammembership.go b/api/dataservices/teammembership/teammembership.go index 7e211df84..cfea717fd 100644 --- a/api/dataservices/teammembership/teammembership.go +++ b/api/dataservices/teammembership/teammembership.go @@ -8,10 +8,8 @@ import ( "github.com/rs/zerolog/log" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "team_membership" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "team_membership" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -34,6 +32,13 @@ func NewService(connection portainer.Connection) (*Service, error) { }, nil } +func (service *Service) Tx(tx portainer.Transaction) ServiceTx { + return ServiceTx{ + service: service, + tx: tx, + } +} + // TeamMembership returns a TeamMembership object by ID func (service *Service) TeamMembership(ID portainer.TeamMembershipID) (*portainer.TeamMembership, error) { var membership portainer.TeamMembership diff --git a/api/dataservices/teammembership/tx.go b/api/dataservices/teammembership/tx.go new file mode 100644 index 000000000..b7fcefeb5 --- /dev/null +++ b/api/dataservices/teammembership/tx.go @@ -0,0 +1,186 @@ +package teammembership + +import ( + "fmt" + + portainer "github.com/portainer/portainer/api" + + "github.com/rs/zerolog/log" +) + +type ServiceTx struct { + service *Service + tx portainer.Transaction +} + +func (service ServiceTx) BucketName() string { + return BucketName +} + +// TeamMembership returns a TeamMembership object by ID +func (service ServiceTx) TeamMembership(ID portainer.TeamMembershipID) (*portainer.TeamMembership, error) { + var membership portainer.TeamMembership + identifier := service.service.connection.ConvertToKey(int(ID)) + + err := service.tx.GetObject(BucketName, identifier, &membership) + if err != nil { + return nil, err + } + + return &membership, nil +} + +// TeamMemberships return an array containing all the TeamMembership objects. +func (service ServiceTx) TeamMemberships() ([]portainer.TeamMembership, error) { + var memberships = make([]portainer.TeamMembership, 0) + + err := 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 +} + +// 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( + 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 +} + +// 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( + 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 +} + +// UpdateTeamMembership saves a TeamMembership object. +func (service ServiceTx) UpdateTeamMembership(ID portainer.TeamMembershipID, membership *portainer.TeamMembership) error { + identifier := service.service.connection.ConvertToKey(int(ID)) + return service.tx.UpdateObject(BucketName, identifier, membership) +} + +// CreateTeamMembership creates a new TeamMembership object. +func (service ServiceTx) Create(membership *portainer.TeamMembership) error { + return service.tx.CreateObject( + BucketName, + func(id uint64) (int, interface{}) { + membership.ID = portainer.TeamMembershipID(id) + return int(membership.ID), membership + }, + ) +} + +// DeleteTeamMembership deletes a TeamMembership object. +func (service ServiceTx) DeleteTeamMembership(ID portainer.TeamMembershipID) error { + identifier := service.service.connection.ConvertToKey(int(ID)) + return service.tx.DeleteObject(BucketName, identifier) +} + +// DeleteTeamMembershipByUserID deletes all the TeamMembership object associated to a UserID. +func (service ServiceTx) DeleteTeamMembershipByUserID(userID portainer.UserID) error { + return service.tx.DeleteAllObjects( + BucketName, + &portainer.TeamMembership{}, + func(obj interface{}) (id int, ok bool) { + membership, ok := obj.(portainer.TeamMembership) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") + //return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) + return -1, false + } + + if membership.UserID == userID { + return int(membership.ID), true + } + + return -1, false + }) +} + +// DeleteTeamMembershipByTeamID deletes all the TeamMembership object associated to a TeamID. +func (service ServiceTx) DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error { + return service.tx.DeleteAllObjects( + BucketName, + &portainer.TeamMembership{}, + func(obj interface{}) (id int, ok bool) { + membership, ok := obj.(portainer.TeamMembership) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") + //return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) + return -1, false + } + + if membership.TeamID == teamID { + return int(membership.ID), true + } + + return -1, false + }) +} + +func (service ServiceTx) DeleteTeamMembershipByTeamIDAndUserID(teamID portainer.TeamID, userID portainer.UserID) error { + return service.tx.DeleteAllObjects( + BucketName, + &portainer.TeamMembership{}, + func(obj interface{}) (id int, ok bool) { + membership, ok := obj.(portainer.TeamMembership) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object") + //return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj) + return -1, false + } + + if membership.TeamID == teamID && membership.UserID == userID { + return int(membership.ID), true + } + + return -1, false + }) +} diff --git a/api/datastore/services_tx.go b/api/datastore/services_tx.go index f912070ff..b7bb5b96a 100644 --- a/api/datastore/services_tx.go +++ b/api/datastore/services_tx.go @@ -67,9 +67,12 @@ func (tx *StoreTx) Tag() dataservices.TagService { return tx.store.TagService.Tx(tx.tx) } -func (tx *StoreTx) TeamMembership() dataservices.TeamMembershipService { return nil } -func (tx *StoreTx) Team() dataservices.TeamService { return nil } -func (tx *StoreTx) TunnelServer() dataservices.TunnelServerService { return nil } -func (tx *StoreTx) User() dataservices.UserService { return nil } -func (tx *StoreTx) Version() dataservices.VersionService { return nil } -func (tx *StoreTx) Webhook() dataservices.WebhookService { return nil } +func (tx *StoreTx) TeamMembership() dataservices.TeamMembershipService { + return tx.store.TeamMembershipService.Tx(tx.tx) +} + +func (tx *StoreTx) Team() dataservices.TeamService { return nil } +func (tx *StoreTx) TunnelServer() dataservices.TunnelServerService { return nil } +func (tx *StoreTx) User() dataservices.UserService { return nil } +func (tx *StoreTx) Version() dataservices.VersionService { return nil } +func (tx *StoreTx) Webhook() dataservices.WebhookService { return nil }