package repository import ( "context" "errors" "time" libmongo "gateway/internal/library/mongo" permission "gateway/internal/model/permission/domain" "gateway/internal/model/permission/domain/entity" "gateway/internal/model/permission/domain/enum" domrepo "gateway/internal/model/permission/domain/repository" "go.mongodb.org/mongo-driver/v2/bson" mongodriver "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // RoleMappingRepositoryParam configures the Mongo role mapping repository. type RoleMappingRepositoryParam struct { Conf *libmongo.Conf } type roleMappingRepository struct { db libmongo.DocumentDBUseCase } // NewRoleMappingRepository creates a Mongo-backed RoleMappingRepository. func NewRoleMappingRepository(param RoleMappingRepositoryParam) domrepo.RoleMappingRepository { documentDB, err := libmongo.NewDocumentDB(param.Conf, entity.RoleMapping{}.CollectionName()) if err != nil { panic(err) } return &roleMappingRepository{db: documentDB} } func (r *roleMappingRepository) Insert(ctx context.Context, rm *entity.RoleMapping) error { now := time.Now().UTC().UnixMilli() if rm.ID.IsZero() { rm.ID = bson.NewObjectID() } if rm.CreateAt == 0 { rm.CreateAt = now } if rm.UpdateAt == 0 { rm.UpdateAt = now } _, err := r.db.GetClient().InsertOne(ctx, rm) if err != nil { if mongodriver.IsDuplicateKeyError(err) { return permission.ErrRoleMappingDuplicate } return err } return nil } func (r *roleMappingRepository) Upsert(ctx context.Context, rm *entity.RoleMapping) error { now := time.Now().UTC().UnixMilli() if rm.UpdateAt == 0 { rm.UpdateAt = now } filter := bson.M{ permission.BSONFieldTenantID: rm.TenantID, permission.BSONFieldExternalSource: rm.ExternalSource, permission.BSONFieldExternalKey: rm.ExternalKey, } set := bson.M{ permission.BSONFieldInternalRoleID: rm.InternalRoleID, permission.BSONFieldInternalRoleKey: rm.InternalRoleKey, permission.BSONFieldUpdateAt: rm.UpdateAt, } insert := bson.M{ permission.BSONFieldTenantID: rm.TenantID, permission.BSONFieldExternalSource: rm.ExternalSource, permission.BSONFieldExternalKey: rm.ExternalKey, permission.BSONFieldCreateAt: now, } _, err := r.db.GetClient().UpdateOne(ctx, filter, bson.M{bsonOpSet: set, bsonOpSetOnInsert: insert}, options.UpdateOne().SetUpsert(true)) return err } func (r *roleMappingRepository) Delete( ctx context.Context, tenantID string, source enum.RoleSource, externalKey string, ) error { filter := bson.M{ permission.BSONFieldTenantID: tenantID, permission.BSONFieldExternalSource: source, permission.BSONFieldExternalKey: externalKey, } res, err := r.db.GetClient().DeleteOne(ctx, filter) if err != nil { return err } if res == 0 { return permission.ErrRoleMappingNotFound } return nil } func (r *roleMappingRepository) DeleteByRole(ctx context.Context, tenantID, roleID string) (int64, error) { filter := bson.M{ permission.BSONFieldTenantID: tenantID, permission.BSONFieldInternalRoleID: roleID, } return r.db.GetClient().DeleteMany(ctx, filter) } func (r *roleMappingRepository) GetByExternal( ctx context.Context, tenantID string, source enum.RoleSource, externalKey string, ) (*entity.RoleMapping, error) { filter := bson.M{ permission.BSONFieldTenantID: tenantID, permission.BSONFieldExternalSource: source, permission.BSONFieldExternalKey: externalKey, } var doc entity.RoleMapping if err := r.db.GetClient().FindOne(ctx, &doc, filter); err != nil { if errors.Is(err, mongodriver.ErrNoDocuments) { return nil, permission.ErrRoleMappingNotFound } return nil, err } return &doc, nil } func (r *roleMappingRepository) ListByTenant( ctx context.Context, tenantID string, source *enum.RoleSource, offset, limit int64, ) ([]*entity.RoleMapping, int64, error) { q := bson.M{permission.BSONFieldTenantID: tenantID} if source != nil { q[permission.BSONFieldExternalSource] = *source } total, err := r.db.GetClient().CountDocuments(ctx, q) if err != nil { return nil, 0, err } if limit <= 0 { limit = 50 } if limit > 200 { limit = 200 } opts := options.Find(). SetSkip(offset). SetLimit(limit). SetSort(bson.D{{Key: permission.BSONFieldCreateAt, Value: -1}}) var docs []*entity.RoleMapping if err := r.db.GetClient().Find(ctx, &docs, q, opts); err != nil { return nil, 0, err } return docs, total, nil } // Index20260521001UP ensures role_mappings collection indexes exist. func (r *roleMappingRepository) Index20260521001UP(ctx context.Context) error { if err := r.db.PopulateMultiIndex(ctx, []string{ permission.BSONFieldTenantID, permission.BSONFieldExternalSource, permission.BSONFieldExternalKey, }, []int32{1, 1, 1}, true); err != nil { return err } return r.db.PopulateMultiIndex(ctx, []string{permission.BSONFieldTenantID, permission.BSONFieldInternalRoleID}, []int32{1, 1}, false) } var _ domrepo.RoleMappingRepository = (*roleMappingRepository)(nil)