270 lines
8.9 KiB
Go
270 lines
8.9 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/tech/sendico/payments/storage"
|
|
"github.com/tech/sendico/pkg/auth"
|
|
"github.com/tech/sendico/pkg/db/repository"
|
|
ri "github.com/tech/sendico/pkg/db/repository/index"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"github.com/tech/sendico/pkg/mlogger"
|
|
pkgmodel "github.com/tech/sendico/pkg/model"
|
|
"github.com/tech/sendico/pkg/mservice"
|
|
mutil "github.com/tech/sendico/pkg/mutil/db"
|
|
mauth "github.com/tech/sendico/pkg/mutil/db/auth"
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type PaymentMethods struct {
|
|
logger mlogger.Logger
|
|
repo repository.Repository
|
|
enforcer auth.Enforcer
|
|
permissionRef bson.ObjectID
|
|
}
|
|
|
|
// NewPaymentMethods constructs a Mongo-backed payment-methods store.
|
|
func NewPaymentMethods(logger mlogger.Logger, repo repository.Repository, enforcer auth.Enforcer, permissionRef bson.ObjectID) (*PaymentMethods, error) {
|
|
if repo == nil {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: repository is nil")
|
|
}
|
|
if enforcer == nil {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: enforcer is nil")
|
|
}
|
|
if permissionRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: permission reference is required")
|
|
}
|
|
|
|
indexes := []*ri.Definition{
|
|
{
|
|
Keys: []ri.Key{
|
|
{Field: "organizationRef", Sort: ri.Asc},
|
|
{Field: "recipientRef", Sort: ri.Asc},
|
|
},
|
|
},
|
|
{
|
|
Keys: []ri.Key{{Field: "recipientRef", Sort: ri.Asc}},
|
|
},
|
|
}
|
|
|
|
for _, def := range indexes {
|
|
if err := repo.CreateIndex(def); err != nil {
|
|
logger.Error("Failed to ensure payment methods index", zap.Error(err), zap.String("collection", repo.Collection()))
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &PaymentMethods{
|
|
logger: logger.Named("payment_methods"),
|
|
repo: repo,
|
|
enforcer: enforcer,
|
|
permissionRef: permissionRef,
|
|
}, nil
|
|
}
|
|
|
|
func (p *PaymentMethods) Create(ctx context.Context, accountRef, organizationRef bson.ObjectID, method *pkgmodel.PaymentMethod) error {
|
|
if method == nil {
|
|
return merrors.InvalidArgument("paymentMethodsStore: nil payment method")
|
|
}
|
|
if accountRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: account_ref is required")
|
|
}
|
|
if organizationRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: organization_ref is required")
|
|
}
|
|
if method.RecipientRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: recipient_ref is required")
|
|
}
|
|
|
|
if method.GetPermissionRef() == bson.NilObjectID {
|
|
method.SetPermissionRef(p.permissionRef)
|
|
}
|
|
method.SetOrganizationRef(organizationRef)
|
|
|
|
allowed, err := p.enforcer.Enforce(ctx, method.GetPermissionRef(), accountRef, organizationRef, bson.NilObjectID, pkgmodel.ActionCreate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !allowed {
|
|
return merrors.AccessDenied(mservice.PaymentMethods, string(pkgmodel.ActionCreate), bson.NilObjectID)
|
|
}
|
|
|
|
return p.repo.Insert(ctx, method, nil)
|
|
}
|
|
|
|
func (p *PaymentMethods) Get(ctx context.Context, accountRef, methodRef bson.ObjectID) (*pkgmodel.PaymentMethod, error) {
|
|
if accountRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: account_ref is required")
|
|
}
|
|
if methodRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: method_ref is required")
|
|
}
|
|
|
|
if err := p.enforceObject(ctx, accountRef, methodRef, pkgmodel.ActionRead); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
method := &pkgmodel.PaymentMethod{}
|
|
if err := p.repo.Get(ctx, methodRef, method); err != nil {
|
|
return nil, err
|
|
}
|
|
return method, nil
|
|
}
|
|
|
|
func (p *PaymentMethods) GetPrivate(ctx context.Context, methodRef bson.ObjectID) (*pkgmodel.PaymentMethod, error) {
|
|
if methodRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: method_ref is required")
|
|
}
|
|
|
|
method := &pkgmodel.PaymentMethod{}
|
|
if err := p.repo.Get(ctx, methodRef, method); err != nil {
|
|
return nil, err
|
|
}
|
|
return method, nil
|
|
}
|
|
|
|
func (p *PaymentMethods) Update(ctx context.Context, accountRef bson.ObjectID, method *pkgmodel.PaymentMethod) error {
|
|
if method == nil {
|
|
return merrors.InvalidArgument("paymentMethodsStore: nil payment method")
|
|
}
|
|
if accountRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: account_ref is required")
|
|
}
|
|
if method.GetID() == nil || method.GetID().IsZero() {
|
|
return merrors.InvalidArgument("paymentMethodsStore: method id is required")
|
|
}
|
|
|
|
if err := p.enforceObject(ctx, accountRef, *method.GetID(), pkgmodel.ActionUpdate); err != nil {
|
|
return err
|
|
}
|
|
|
|
return p.repo.Update(ctx, method)
|
|
}
|
|
|
|
func (p *PaymentMethods) Delete(ctx context.Context, accountRef, methodRef bson.ObjectID) error {
|
|
if accountRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: account_ref is required")
|
|
}
|
|
if methodRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: method_ref is required")
|
|
}
|
|
|
|
if err := p.enforceObject(ctx, accountRef, methodRef, pkgmodel.ActionDelete); err != nil {
|
|
return err
|
|
}
|
|
|
|
return p.repo.Delete(ctx, methodRef)
|
|
}
|
|
|
|
func (p *PaymentMethods) DeleteCascade(ctx context.Context, accountRef, methodRef bson.ObjectID) error {
|
|
return p.Delete(ctx, accountRef, methodRef)
|
|
}
|
|
|
|
func (p *PaymentMethods) SetArchived(ctx context.Context, accountRef, _ bson.ObjectID, methodRef bson.ObjectID, archived, _ bool) error {
|
|
if accountRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: account_ref is required")
|
|
}
|
|
if methodRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: method_ref is required")
|
|
}
|
|
|
|
if err := p.enforceObject(ctx, accountRef, methodRef, pkgmodel.ActionUpdate); err != nil {
|
|
return err
|
|
}
|
|
|
|
patch := repository.Patch().Set(repository.Field("isArchived"), archived)
|
|
return p.repo.Patch(ctx, methodRef, patch)
|
|
}
|
|
|
|
func (p *PaymentMethods) List(ctx context.Context, accountRef, organizationRef, recipientRef bson.ObjectID, cursor *pkgmodel.ViewCursor) ([]pkgmodel.PaymentMethod, error) {
|
|
if accountRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: account_ref is required")
|
|
}
|
|
if organizationRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: organization_ref is required")
|
|
}
|
|
if recipientRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: recipient_ref is required")
|
|
}
|
|
|
|
items, err := mauth.GetProtectedObjects[pkgmodel.PaymentMethod](
|
|
ctx,
|
|
p.logger,
|
|
accountRef,
|
|
organizationRef,
|
|
pkgmodel.ActionRead,
|
|
repository.OrgFilter(organizationRef).And(repository.Filter("recipientRef", recipientRef)),
|
|
cursor,
|
|
p.enforcer,
|
|
p.repo,
|
|
)
|
|
if errors.Is(err, merrors.ErrNoData) {
|
|
return []pkgmodel.PaymentMethod{}, nil
|
|
}
|
|
return items, err
|
|
}
|
|
|
|
func (p *PaymentMethods) ListPrivate(ctx context.Context, organizationRef, recipientRef bson.ObjectID, cursor *pkgmodel.ViewCursor) ([]pkgmodel.PaymentMethod, error) {
|
|
if organizationRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: organization_ref is required")
|
|
}
|
|
if recipientRef == bson.NilObjectID {
|
|
return nil, merrors.InvalidArgument("paymentMethodsStore: recipient_ref is required")
|
|
}
|
|
|
|
items, err := mutil.GetObjects[pkgmodel.PaymentMethod](
|
|
ctx,
|
|
p.logger,
|
|
repository.OrgFilter(organizationRef).And(repository.Filter("recipientRef", recipientRef)),
|
|
cursor,
|
|
p.repo,
|
|
)
|
|
if errors.Is(err, merrors.ErrNoData) {
|
|
return []pkgmodel.PaymentMethod{}, nil
|
|
}
|
|
return items, err
|
|
}
|
|
|
|
func (p *PaymentMethods) SetArchivedByRecipient(ctx context.Context, recipientRef bson.ObjectID, archived bool) (int, error) {
|
|
if recipientRef == bson.NilObjectID {
|
|
return 0, merrors.InvalidArgument("paymentMethodsStore: recipient_ref is required")
|
|
}
|
|
|
|
filter := repository.Filter("recipientRef", recipientRef)
|
|
patch := repository.Patch().Set(repository.Field("isArchived"), archived)
|
|
return p.repo.PatchMany(ctx, filter, patch)
|
|
}
|
|
|
|
func (p *PaymentMethods) DeleteByRecipient(ctx context.Context, recipientRef bson.ObjectID) error {
|
|
if recipientRef == bson.NilObjectID {
|
|
return merrors.InvalidArgument("paymentMethodsStore: recipient_ref is required")
|
|
}
|
|
return p.repo.DeleteMany(ctx, repository.Filter("recipientRef", recipientRef))
|
|
}
|
|
|
|
func (p *PaymentMethods) enforceObject(ctx context.Context, accountRef, methodRef bson.ObjectID, action pkgmodel.Action) error {
|
|
refs, err := p.repo.ListPermissionBound(ctx, repository.IDFilter(methodRef))
|
|
if err != nil {
|
|
if errors.Is(err, merrors.ErrNoData) {
|
|
return merrors.AccessDenied(mservice.PaymentMethods, string(action), methodRef)
|
|
}
|
|
return err
|
|
}
|
|
if len(refs) == 0 {
|
|
return merrors.AccessDenied(mservice.PaymentMethods, string(action), methodRef)
|
|
}
|
|
|
|
allowed, err := p.enforcer.Enforce(ctx, refs[0].GetPermissionRef(), accountRef, refs[0].GetOrganizationRef(), methodRef, action)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !allowed {
|
|
return merrors.AccessDenied(mservice.PaymentMethods, string(action), methodRef)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ storage.PaymentMethodsStore = (*PaymentMethods)(nil)
|