Files
sendico/api/pkg/db/internal/mongo/invitationdb/getpublic.go
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

122 lines
3.8 KiB
Go

package invitationdb
import (
"context"
"fmt"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
"github.com/tech/sendico/pkg/mutil/mzap"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
)
func (db *InvitationDB) GetPublic(ctx context.Context, invitationRef primitive.ObjectID) (*model.PublicInvitation, error) {
roleField := repository.Field("role")
orgField := repository.Field("organization")
accField := repository.Field("account")
empField := repository.Field("employee")
regField := repository.Field("registrationAcc")
descEmailField := repository.Field("description").Dot("email")
pipeline := repository.Pipeline().
// 0) Filter to exactly the invitation(s) you want
Match(repository.IDFilter(invitationRef).And(repository.Filter("status", model.InvitationCreated))).
// 1) Lookup the role document
Lookup(
mservice.Roles,
repository.Field("roleRef"),
repository.IDField(),
roleField,
).
Unwind(repository.Ref(roleField)).
// 2) Lookup the organization document
Lookup(
mservice.Organizations,
repository.Field("organizationRef"),
repository.IDField(),
orgField,
).
Unwind(repository.Ref(orgField)).
// 3) Lookup the account document
Lookup(
mservice.Accounts,
repository.Field("inviterRef"),
repository.IDField(),
accField,
).
Unwind(repository.Ref(accField)).
/* 4) do we already have an account whose login == invitation.description ? */
Lookup(
mservice.Accounts,
descEmailField, // local field (invitation.description.email)
repository.Field("login"), // foreign field (account.login)
regField, // array: 0-length or ≥1
).
// 5) Projection
Project(
repository.SimpleAlias(
empField.Dot("description"),
repository.Ref(accField),
),
repository.SimpleAlias(
empField.Dot("avatarUrl"),
repository.Ref(accField.Dot("avatarUrl")),
),
repository.SimpleAlias(
orgField.Dot("description"),
repository.Ref(orgField),
),
repository.SimpleAlias(
orgField.Dot("logoUrl"),
repository.Ref(orgField.Dot("logoUrl")),
),
repository.SimpleAlias(
roleField,
repository.Ref(roleField),
),
repository.SimpleAlias(
repository.Field("invitation"), // ← left-hand side
repository.Ref(repository.Field("description")), // ← right-hand side (“$description”)
),
repository.SimpleAlias(
repository.Field("storable"), // ← left-hand side
repository.RootRef(), // ← right-hand side (“$description”)
),
repository.ProjectionExpr(
repository.Field("registrationRequired"),
repository.Eq(
repository.Size(repository.Value(repository.Ref(regField).Build())),
repository.Literal(0),
),
),
)
var res model.PublicInvitation
haveResult := false
decoder := func(cur *mongo.Cursor) error {
if haveResult {
// should never get here
db.DBImp.Logger.Warn("Unexpected extra invitation", mzap.ObjRef("invitation_ref", invitationRef))
return merrors.Internal("Unexpected extra invitation found by reference")
}
if e := cur.Decode(&res); e != nil {
db.DBImp.Logger.Warn("Failed to decode entity", zap.Error(e), zap.Any("data", cur.Current.String()))
return e
}
haveResult = true
return nil
}
if err := db.DBImp.Repository.Aggregate(ctx, pipeline, decoder); err != nil {
db.DBImp.Logger.Warn("Failed to execute aggregation pipeline", zap.Error(err), mzap.ObjRef("invitation_ref", invitationRef))
return nil, err
}
if !haveResult {
db.DBImp.Logger.Warn("No results fetched", mzap.ObjRef("invitation_ref", invitationRef))
return nil, merrors.NoData(fmt.Sprintf("Invitation %s not found", invitationRef.Hex()))
}
return &res, nil
}