package organization import ( "context" "errors" "git.sendico.io/tech/sxchnge/bff/pkg/db/repository" "git.sendico.io/tech/sxchnge/bff/pkg/db/repository/builder" "git.sendico.io/tech/sxchnge/bff/pkg/merrors" "git.sendico.io/tech/sxchnge/bff/pkg/model" "git.sendico.io/tech/sxchnge/bff/pkg/mutil/mzap" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) func (db *OrganizationDB) IsMember(ctx context.Context, orgRef, callerRef bson.ObjectID) (bool, error) { db.DBImp.Logger.Debug("Checking organization membership", mzap.OrgRef(orgRef), mzap.AccRef(callerRef)) filter := repository.Query(). Filter(repository.IDField(), orgRef). Comparison(repository.Field("members"), builder.Eq, callerRef) var org model.Organization if err := db.Unprotected().FindOne(ctx, filter, &org); err != nil { if errors.Is(err, merrors.ErrNoData) { return false, nil } db.DBImp.Logger.Warn("Membership check failed", zap.Error(err), mzap.OrgRef(orgRef), mzap.AccRef(callerRef)) return false, err } return true, nil } // AddMember atomically appends the account to the members list and reports // false when it already was one. A point patch on purpose: replacing the whole // document would clobber concurrent writes to fields the caller never read, // most notably the provisioning lease. func (db *OrganizationDB) AddMember(ctx context.Context, orgRef, accountRef bson.ObjectID) (bool, error) { filter := repository.Query(). Filter(repository.IDField(), orgRef). Comparison(repository.Field("members"), builder.Ne, accountRef) patch := repository.Patch().AddToSet(repository.Field("members"), accountRef) matched, err := db.DBImp.PatchMany(ctx, filter, patch) if err != nil { db.DBImp.Logger.Warn("Failed to add organization member", zap.Error(err), mzap.OrgRef(orgRef), mzap.AccRef(accountRef)) return false, err } return matched > 0, nil }