package store import ( "context" "errors" "strings" "github.com/tech/sendico/payments/storage" "github.com/tech/sendico/payments/storage/model" "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" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.uber.org/zap" ) type Routes struct { logger mlogger.Logger repo repository.Repository } // NewRoutes constructs a Mongo-backed routes store. func NewRoutes(logger mlogger.Logger, repo repository.Repository) (*Routes, error) { if repo == nil { return nil, merrors.InvalidArgument("routesStore: repository is nil") } indexes := []*ri.Definition{ { Keys: []ri.Key{ {Field: "fromRail", Sort: ri.Asc}, {Field: "toRail", Sort: ri.Asc}, {Field: "network", Sort: ri.Asc}, }, Unique: true, }, { Keys: []ri.Key{{Field: "fromRail", Sort: ri.Asc}}, }, { Keys: []ri.Key{{Field: "toRail", Sort: ri.Asc}}, }, { Keys: []ri.Key{{Field: "isEnabled", Sort: ri.Asc}}, }, } for _, def := range indexes { if err := repo.CreateIndex(def); err != nil { logger.Error("Failed to ensure routes index", zap.Error(err), zap.String("collection", repo.Collection())) return nil, err } } return &Routes{ logger: logger.Named("routes"), repo: repo, }, nil } func (r *Routes) Create(ctx context.Context, route *model.PaymentRoute) error { if route == nil { return merrors.InvalidArgument("routesStore: nil route") } route.Normalize() if route.FromRail == "" || route.FromRail == model.RailUnspecified { return merrors.InvalidArgument("routesStore: from_rail is required") } if route.ToRail == "" || route.ToRail == model.RailUnspecified { return merrors.InvalidArgument("routesStore: to_rail is required") } if route.ID.IsZero() { route.SetID(bson.NewObjectID()) } else { route.Update() } filter := repository.Filter("fromRail", route.FromRail).And( repository.Filter("toRail", route.ToRail), repository.Filter("network", route.Network), ) if err := r.repo.Insert(ctx, route, filter); err != nil { if errors.Is(err, merrors.ErrDataConflict) { return storage.ErrDuplicateRoute } return err } return nil } func (r *Routes) Update(ctx context.Context, route *model.PaymentRoute) error { if route == nil { return merrors.InvalidArgument("routesStore: nil route") } if route.ID.IsZero() { return merrors.InvalidArgument("routesStore: missing route id") } route.Normalize() route.Update() if err := r.repo.Update(ctx, route); err != nil { if errors.Is(err, merrors.ErrNoData) { return storage.ErrRouteNotFound } return err } return nil } func (r *Routes) GetByID(ctx context.Context, id bson.ObjectID) (*model.PaymentRoute, error) { if id == bson.NilObjectID { return nil, merrors.InvalidArgument("routesStore: route id is required") } entity := &model.PaymentRoute{} if err := r.repo.Get(ctx, id, entity); err != nil { if errors.Is(err, merrors.ErrNoData) { return nil, storage.ErrRouteNotFound } return nil, err } return entity, nil } func (r *Routes) List(ctx context.Context, filter *model.PaymentRouteFilter) (*model.PaymentRouteList, error) { if filter == nil { filter = &model.PaymentRouteFilter{} } query := repository.Query() if from := strings.ToUpper(strings.TrimSpace(string(filter.FromRail))); from != "" { query = query.Filter(repository.Field("fromRail"), from) } if to := strings.ToUpper(strings.TrimSpace(string(filter.ToRail))); to != "" { query = query.Filter(repository.Field("toRail"), to) } if network := strings.ToUpper(strings.TrimSpace(filter.Network)); network != "" { query = query.Filter(repository.Field("network"), network) } if filter.IsEnabled != nil { query = query.Filter(repository.Field("isEnabled"), *filter.IsEnabled) } routes := make([]*model.PaymentRoute, 0) decoder := func(cur *mongo.Cursor) error { item := &model.PaymentRoute{} if err := cur.Decode(item); err != nil { return err } routes = append(routes, item) return nil } if err := r.repo.FindManyByFilter(ctx, query, decoder); err != nil && !errors.Is(err, merrors.ErrNoData) { return nil, err } return &model.PaymentRouteList{ Items: routes, }, nil } var _ storage.RoutesStore = (*Routes)(nil)