Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,82 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/add_member.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type AddCollectionMemberUseCase interface {
|
||||
Execute(ctx context.Context, collectionID gocql.UUID, membership *dom_collection.CollectionMembership) error
|
||||
}
|
||||
|
||||
type addCollectionMemberUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewAddCollectionMemberUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AddCollectionMemberUseCase {
|
||||
logger = logger.Named("AddCollectionMemberUseCase")
|
||||
return &addCollectionMemberUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *addCollectionMemberUseCaseImpl) Execute(ctx context.Context, collectionID gocql.UUID, membership *dom_collection.CollectionMembership) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if membership == nil {
|
||||
e["membership"] = "Membership details are required"
|
||||
} else {
|
||||
// Generate member ID if not provided
|
||||
if membership.ID.String() == "" || membership.ID.String() == "00000000-0000-0000-0000-000000000000" {
|
||||
membership.ID = gocql.TimeUUID()
|
||||
}
|
||||
|
||||
if membership.RecipientID.String() == "" {
|
||||
e["recipient_id"] = "Recipient ID is required"
|
||||
}
|
||||
if membership.RecipientEmail == "" {
|
||||
e["recipient_email"] = "Recipient email is required"
|
||||
}
|
||||
if membership.GrantedByID.String() == "" {
|
||||
e["granted_by_id"] = "Granted by ID is required"
|
||||
}
|
||||
if len(membership.EncryptedCollectionKey) == 0 {
|
||||
e["encrypted_collection_key"] = "Encrypted collection key is required"
|
||||
}
|
||||
if membership.PermissionLevel == "" {
|
||||
// Default permission level will be set in the repository
|
||||
} else if membership.PermissionLevel != dom_collection.CollectionPermissionReadOnly &&
|
||||
membership.PermissionLevel != dom_collection.CollectionPermissionReadWrite &&
|
||||
membership.PermissionLevel != dom_collection.CollectionPermissionAdmin {
|
||||
e["permission_level"] = "Invalid permission level"
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating add collection member",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Add member to collection.
|
||||
//
|
||||
|
||||
return uc.repo.AddMember(ctx, collectionID, membership)
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/add_member_to_hierarchy.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type AddMemberToHierarchyUseCase interface {
|
||||
Execute(ctx context.Context, rootID gocql.UUID, membership *dom_collection.CollectionMembership) error
|
||||
}
|
||||
|
||||
type addMemberToHierarchyUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewAddMemberToHierarchyUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AddMemberToHierarchyUseCase {
|
||||
logger = logger.Named("AddMemberToHierarchyUseCase")
|
||||
return &addMemberToHierarchyUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *addMemberToHierarchyUseCaseImpl) Execute(ctx context.Context, rootID gocql.UUID, membership *dom_collection.CollectionMembership) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if rootID.String() == "" {
|
||||
e["root_id"] = "Root collection ID is required"
|
||||
}
|
||||
if membership == nil {
|
||||
e["membership"] = "Membership details are required"
|
||||
} else {
|
||||
// Generate member ID if not provided
|
||||
if membership.ID.String() == "" || membership.ID.String() == "00000000-0000-0000-0000-000000000000" {
|
||||
membership.ID = gocql.TimeUUID()
|
||||
}
|
||||
|
||||
if membership.RecipientID.String() == "" {
|
||||
e["recipient_id"] = "Recipient ID is required"
|
||||
}
|
||||
if membership.RecipientEmail == "" {
|
||||
e["recipient_email"] = "Recipient email is required"
|
||||
}
|
||||
if membership.GrantedByID.String() == "" {
|
||||
e["granted_by_id"] = "Granted by ID is required"
|
||||
}
|
||||
if len(membership.EncryptedCollectionKey) == 0 {
|
||||
e["encrypted_collection_key"] = "Encrypted collection key is required"
|
||||
}
|
||||
if membership.PermissionLevel == "" {
|
||||
// Default permission level will be set in the repository
|
||||
} else if membership.PermissionLevel != dom_collection.CollectionPermissionReadOnly &&
|
||||
membership.PermissionLevel != dom_collection.CollectionPermissionReadWrite &&
|
||||
membership.PermissionLevel != dom_collection.CollectionPermissionAdmin {
|
||||
e["permission_level"] = "Invalid permission level"
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating add member to hierarchy",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Add member to collection hierarchy.
|
||||
//
|
||||
|
||||
return uc.repo.AddMemberToHierarchy(ctx, rootID, membership)
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// monorepo/cloud/backend/internal/usecase/collection/anonymize_old_ips.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
)
|
||||
|
||||
type AnonymizeOldIPsUseCase interface {
|
||||
Execute(ctx context.Context, cutoffDate time.Time) (int, error)
|
||||
}
|
||||
|
||||
type anonymizeOldIPsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewAnonymizeOldIPsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AnonymizeOldIPsUseCase {
|
||||
logger = logger.Named("CollectionAnonymizeOldIPsUseCase")
|
||||
return &anonymizeOldIPsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *anonymizeOldIPsUseCaseImpl) Execute(ctx context.Context, cutoffDate time.Time) (int, error) {
|
||||
uc.logger.Debug("Anonymizing old IPs in collection tables",
|
||||
zap.Time("cutoff_date", cutoffDate))
|
||||
|
||||
count, err := uc.repo.AnonymizeOldIPs(ctx, cutoffDate)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to anonymize old IPs in collection tables",
|
||||
zap.Error(err),
|
||||
zap.Time("cutoff_date", cutoffDate))
|
||||
return 0, err
|
||||
}
|
||||
|
||||
uc.logger.Info("Successfully anonymized old IPs in collection tables",
|
||||
zap.Int("count", count),
|
||||
zap.Time("cutoff_date", cutoffDate))
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/collection/anonymize_user_references.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
)
|
||||
|
||||
// AnonymizeUserReferencesUseCase handles anonymizing CreatedByUserID and ModifiedByUserID
|
||||
// references when a user is deleted, replacing them with a special "deleted user" UUID.
|
||||
type AnonymizeUserReferencesUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) (int, error)
|
||||
}
|
||||
|
||||
type anonymizeUserReferencesUseCaseImpl struct {
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
// NewAnonymizeUserReferencesUseCase creates a new use case for anonymizing user references in collections
|
||||
func NewAnonymizeUserReferencesUseCase(
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AnonymizeUserReferencesUseCase {
|
||||
return &anonymizeUserReferencesUseCaseImpl{
|
||||
logger: logger,
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
// DeletedUserUUID is a well-known UUID representing a deleted user
|
||||
// UUID: 00000000-0000-0000-0000-000000000001 (DELETED_USER)
|
||||
var DeletedUserUUID = gocql.UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
|
||||
func (uc *anonymizeUserReferencesUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) (int, error) {
|
||||
uc.logger.Info("Anonymizing user references in collection metadata",
|
||||
zap.String("user_id", userID.String()))
|
||||
|
||||
// Get all collections owned by this user
|
||||
collections, err := uc.repo.GetAllByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get collections by owner",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return 0, fmt.Errorf("failed to get collections by owner: %w", err)
|
||||
}
|
||||
|
||||
updatedCount := 0
|
||||
|
||||
// Update each collection to replace user references with deleted user UUID
|
||||
for _, collection := range collections {
|
||||
needsUpdate := false
|
||||
|
||||
// Check if this collection has references to the deleted user
|
||||
if collection.CreatedByUserID == userID {
|
||||
collection.CreatedByUserID = DeletedUserUUID
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
if collection.ModifiedByUserID == userID {
|
||||
collection.ModifiedByUserID = DeletedUserUUID
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
// Also anonymize GrantedByID in collection memberships
|
||||
for i := range collection.Members {
|
||||
if collection.Members[i].GrantedByID == userID {
|
||||
collection.Members[i].GrantedByID = DeletedUserUUID
|
||||
needsUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
// Update the collection with anonymized references
|
||||
if err := uc.repo.Update(ctx, collection); err != nil {
|
||||
uc.logger.Error("Failed to anonymize user references in collection",
|
||||
zap.String("collection_id", collection.ID.String()),
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
// Continue with other collections even if one fails
|
||||
continue
|
||||
}
|
||||
updatedCount++
|
||||
}
|
||||
}
|
||||
|
||||
uc.logger.Info("✅ Anonymized user references in collection metadata",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("collections_updated", updatedCount))
|
||||
|
||||
return updatedCount, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/collection/archive.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type ArchiveCollectionUseCase interface {
|
||||
Execute(ctx context.Context, id gocql.UUID) error
|
||||
}
|
||||
|
||||
type archiveCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewArchiveCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) ArchiveCollectionUseCase {
|
||||
logger = logger.Named("ArchiveCollectionUseCase")
|
||||
return &archiveCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *archiveCollectionUseCaseImpl) Execute(ctx context.Context, id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection archival",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Archive collection using repository method.
|
||||
//
|
||||
|
||||
return uc.repo.Archive(ctx, id)
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/check_access.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type CheckCollectionAccessUseCase interface {
|
||||
Execute(ctx context.Context, collectionID, userID gocql.UUID, requiredPermission string) (bool, error)
|
||||
}
|
||||
|
||||
type checkCollectionAccessUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewCheckCollectionAccessUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CheckCollectionAccessUseCase {
|
||||
logger = logger.Named("CheckCollectionAccessUseCase")
|
||||
return &checkCollectionAccessUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *checkCollectionAccessUseCaseImpl) Execute(ctx context.Context, collectionID, userID gocql.UUID, requiredPermission string) (bool, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if requiredPermission == "" {
|
||||
// Default to read-only if not specified
|
||||
requiredPermission = dom_collection.CollectionPermissionReadOnly
|
||||
} else if requiredPermission != dom_collection.CollectionPermissionReadOnly &&
|
||||
requiredPermission != dom_collection.CollectionPermissionReadWrite &&
|
||||
requiredPermission != dom_collection.CollectionPermissionAdmin {
|
||||
e["required_permission"] = "Invalid permission level"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating check collection access",
|
||||
zap.Any("error", e))
|
||||
return false, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Check access.
|
||||
//
|
||||
|
||||
return uc.repo.CheckAccess(ctx, collectionID, userID, requiredPermission)
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/collection/count_collections.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// CountCollectionsResponse contains the collection counts for a user
|
||||
type CountCollectionsResponse struct {
|
||||
OwnedCollections int `json:"owned_collections"`
|
||||
SharedCollections int `json:"shared_collections"`
|
||||
TotalCollections int `json:"total_collections"`
|
||||
}
|
||||
|
||||
// CountFoldersResponse contains the folder counts for a user (folders only, not albums)
|
||||
type CountFoldersResponse struct {
|
||||
OwnedFolders int `json:"owned_folders"`
|
||||
SharedFolders int `json:"shared_folders"`
|
||||
TotalFolders int `json:"total_folders"`
|
||||
}
|
||||
|
||||
type CountUserCollectionsUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) (*CountCollectionsResponse, error)
|
||||
}
|
||||
|
||||
// NEW: Use case specifically for counting folders only
|
||||
type CountUserFoldersUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) (*CountFoldersResponse, error)
|
||||
}
|
||||
|
||||
type countUserCollectionsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
type countUserFoldersUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewCountUserCollectionsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CountUserCollectionsUseCase {
|
||||
logger = logger.Named("CountUserCollectionsUseCase")
|
||||
return &countUserCollectionsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func NewCountUserFoldersUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CountUserFoldersUseCase {
|
||||
logger = logger.Named("CountUserFoldersUseCase")
|
||||
return &countUserFoldersUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *countUserCollectionsUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) (*CountCollectionsResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating count user collections",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Count collections.
|
||||
//
|
||||
|
||||
ownedCollections, err := uc.repo.CountOwnedCollections(ctx, userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to count owned collections",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sharedCollections, err := uc.repo.CountSharedCollections(ctx, userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to count shared collections",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &CountCollectionsResponse{
|
||||
OwnedCollections: ownedCollections,
|
||||
SharedCollections: sharedCollections,
|
||||
TotalCollections: ownedCollections + sharedCollections,
|
||||
}
|
||||
|
||||
uc.logger.Debug("Successfully counted user collections",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("owned_collections", ownedCollections),
|
||||
zap.Int("shared_collections", sharedCollections),
|
||||
zap.Int("total_collections", response.TotalCollections))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (uc *countUserFoldersUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) (*CountFoldersResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating count user folders",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: DEBUG - Check what's actually in the database
|
||||
//
|
||||
|
||||
// ADD DEBUG LOGGING - Cast to concrete type to access debug method
|
||||
if debugRepo, ok := uc.repo.(interface {
|
||||
DebugCollectionRecords(context.Context, gocql.UUID) error
|
||||
}); ok {
|
||||
if debugErr := debugRepo.DebugCollectionRecords(ctx, userID); debugErr != nil {
|
||||
uc.logger.Warn("Failed to debug collection records", zap.Error(debugErr))
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 3: Count folders with separate owned/shared counts AND total unique count
|
||||
//
|
||||
|
||||
ownedFolders, err := uc.repo.CountOwnedFolders(ctx, userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to count owned folders",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sharedFolders, err := uc.repo.CountSharedFolders(ctx, userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to count shared folders",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NEW: Get the deduplicated total count
|
||||
var totalUniqueFolders int
|
||||
if uniqueRepo, ok := uc.repo.(interface {
|
||||
CountTotalUniqueFolders(context.Context, gocql.UUID) (int, error)
|
||||
}); ok {
|
||||
totalUniqueFolders, err = uniqueRepo.CountTotalUniqueFolders(ctx, userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to count unique total folders",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Fallback to simple addition if the method is not available
|
||||
uc.logger.Warn("CountTotalUniqueFolders method not available, using simple addition")
|
||||
totalUniqueFolders = ownedFolders + sharedFolders
|
||||
}
|
||||
|
||||
response := &CountFoldersResponse{
|
||||
OwnedFolders: ownedFolders,
|
||||
SharedFolders: sharedFolders,
|
||||
TotalFolders: totalUniqueFolders, // Use deduplicated count
|
||||
}
|
||||
|
||||
uc.logger.Info("Successfully counted user folders with deduplication",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("owned_folders", ownedFolders),
|
||||
zap.Int("shared_folders", sharedFolders),
|
||||
zap.Int("total_unique_folders", totalUniqueFolders),
|
||||
zap.Int("would_be_simple_sum", ownedFolders+sharedFolders))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/create.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type CreateCollectionUseCase interface {
|
||||
Execute(ctx context.Context, collection *dom_collection.Collection) error
|
||||
}
|
||||
|
||||
type createCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewCreateCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CreateCollectionUseCase {
|
||||
logger = logger.Named("CreateCollectionUseCase")
|
||||
return &createCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *createCollectionUseCaseImpl) Execute(ctx context.Context, collection *dom_collection.Collection) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collection == nil {
|
||||
e["collection"] = "Collection is required"
|
||||
} else {
|
||||
if collection.OwnerID.String() == "" {
|
||||
e["owner_id"] = "Owner ID is required"
|
||||
}
|
||||
if collection.EncryptedName == "" {
|
||||
e["encrypted_name"] = "Collection name is required"
|
||||
}
|
||||
if collection.CollectionType == "" {
|
||||
e["collection_type"] = "Collection type is required"
|
||||
} else if collection.CollectionType != dom_collection.CollectionTypeFolder && collection.CollectionType != dom_collection.CollectionTypeAlbum {
|
||||
e["collection_type"] = "Collection type must be either 'folder' or 'album'"
|
||||
}
|
||||
if collection.EncryptedCollectionKey.Ciphertext == nil || len(collection.EncryptedCollectionKey.Ciphertext) == 0 {
|
||||
e["encrypted_collection_key"] = "Encrypted collection key is required"
|
||||
}
|
||||
if collection.State == "" {
|
||||
e["state"] = "File state is required"
|
||||
} else if collection.State != dom_collection.CollectionStateActive &&
|
||||
collection.State != dom_collection.CollectionStateDeleted &&
|
||||
collection.State != dom_collection.CollectionStateArchived {
|
||||
e["state"] = "Invalid collection state"
|
||||
}
|
||||
if err := dom_collection.IsValidStateTransition(dom_collection.CollectionStateActive, collection.State); err != nil {
|
||||
e["state"] = err.Error()
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection creation",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Insert into database.
|
||||
//
|
||||
|
||||
return uc.repo.Create(ctx, collection)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/find_by_parent.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type FindCollectionsByParentUseCase interface {
|
||||
Execute(ctx context.Context, parentID gocql.UUID) ([]*dom_collection.Collection, error)
|
||||
}
|
||||
|
||||
type findCollectionsByParentUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewFindCollectionsByParentUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) FindCollectionsByParentUseCase {
|
||||
logger = logger.Named("FindCollectionsByParentUseCase")
|
||||
return &findCollectionsByParentUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *findCollectionsByParentUseCaseImpl) Execute(ctx context.Context, parentID gocql.UUID) ([]*dom_collection.Collection, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if parentID.String() == "" {
|
||||
e["parent_id"] = "Parent ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating find collections by parent",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Find collections by parent.
|
||||
//
|
||||
|
||||
return uc.repo.FindByParent(ctx, parentID)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/find_descendants.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type FindDescendantsUseCase interface {
|
||||
Execute(ctx context.Context, collectionID gocql.UUID) ([]*dom_collection.Collection, error)
|
||||
}
|
||||
|
||||
type findDescendantsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewFindDescendantsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) FindDescendantsUseCase {
|
||||
logger = logger.Named("FindDescendantsUseCase")
|
||||
return &findDescendantsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *findDescendantsUseCaseImpl) Execute(ctx context.Context, collectionID gocql.UUID) ([]*dom_collection.Collection, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating find descendants",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Find descendants.
|
||||
//
|
||||
|
||||
return uc.repo.FindDescendants(ctx, collectionID)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/find_root_collections.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type FindRootCollectionsUseCase interface {
|
||||
Execute(ctx context.Context, ownerID gocql.UUID) ([]*dom_collection.Collection, error)
|
||||
}
|
||||
|
||||
type findRootCollectionsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewFindRootCollectionsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) FindRootCollectionsUseCase {
|
||||
logger = logger.Named("FindRootCollectionsUseCase")
|
||||
return &findRootCollectionsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *findRootCollectionsUseCaseImpl) Execute(ctx context.Context, ownerID gocql.UUID) ([]*dom_collection.Collection, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if ownerID.String() == "" {
|
||||
e["owner_id"] = "Owner ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating find root collections",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Find root collections.
|
||||
//
|
||||
|
||||
return uc.repo.FindRootCollections(ctx, ownerID)
|
||||
}
|
||||
65
cloud/maplefile-backend/internal/usecase/collection/get.go
Normal file
65
cloud/maplefile-backend/internal/usecase/collection/get.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/get.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetCollectionUseCase interface {
|
||||
Execute(ctx context.Context, id gocql.UUID) (*dom_collection.Collection, error)
|
||||
}
|
||||
|
||||
type getCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewGetCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) GetCollectionUseCase {
|
||||
logger = logger.Named("GetCollectionUseCase")
|
||||
return &getCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getCollectionUseCaseImpl) Execute(ctx context.Context, id gocql.UUID) (*dom_collection.Collection, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection retrieval",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
collection, err := uc.repo.Get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if collection == nil {
|
||||
uc.logger.Debug("Collection not found",
|
||||
zap.Any("id", id))
|
||||
return nil, httperror.NewForNotFoundWithSingleField("message", "Collection not found")
|
||||
}
|
||||
|
||||
return collection, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/get_filtered.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetFilteredCollectionsUseCase interface {
|
||||
Execute(ctx context.Context, options dom_collection.CollectionFilterOptions) (*dom_collection.CollectionFilterResult, error)
|
||||
}
|
||||
|
||||
type getFilteredCollectionsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewGetFilteredCollectionsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) GetFilteredCollectionsUseCase {
|
||||
logger = logger.Named("GetFilteredCollectionsUseCase")
|
||||
return &getFilteredCollectionsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getFilteredCollectionsUseCaseImpl) Execute(ctx context.Context, options dom_collection.CollectionFilterOptions) (*dom_collection.CollectionFilterResult, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if options.UserID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if !options.IsValid() {
|
||||
e["filter_options"] = "At least one filter option (include_owned or include_shared) must be enabled"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating get filtered collections",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get filtered collections from repository.
|
||||
//
|
||||
|
||||
result, err := uc.repo.GetCollectionsWithFilter(ctx, options)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get filtered collections from repository",
|
||||
zap.Any("error", err),
|
||||
zap.Any("options", options))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uc.logger.Debug("Successfully retrieved filtered collections",
|
||||
zap.Int("owned_count", len(result.OwnedCollections)),
|
||||
zap.Int("shared_count", len(result.SharedCollections)),
|
||||
zap.Int("total_count", result.TotalCount),
|
||||
zap.Any("user_id", options.UserID))
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/get_sync_data.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
)
|
||||
|
||||
type GetCollectionSyncDataUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID, cursor *dom_collection.CollectionSyncCursor, limit int64, accessType string) (*dom_collection.CollectionSyncResponse, error)
|
||||
}
|
||||
|
||||
type getCollectionSyncDataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewGetCollectionSyncDataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) GetCollectionSyncDataUseCase {
|
||||
logger = logger.Named("GetCollectionSyncDataUseCase")
|
||||
return &getCollectionSyncDataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getCollectionSyncDataUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID, cursor *dom_collection.CollectionSyncCursor, limit int64, accessType string) (*dom_collection.CollectionSyncResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
// (Skip)
|
||||
|
||||
//
|
||||
// STEP 2: Get filtered collections from repository.
|
||||
//
|
||||
|
||||
if accessType != dom_collection.CollectionAccessTypeMember && accessType != dom_collection.CollectionAccessTypeOwner {
|
||||
result, err := uc.repo.GetCollectionSyncData(ctx, userID, cursor, limit)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get filtered collections from repository",
|
||||
zap.Any("error", err),
|
||||
zap.Any("userID", userID),
|
||||
zap.Any("cursor", cursor),
|
||||
zap.Int64("limit", limit))
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
result, err := uc.repo.GetCollectionSyncDataByAccessType(ctx, userID, cursor, limit, accessType)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get filtered collections from repository",
|
||||
zap.Any("error", err),
|
||||
zap.Any("userID", userID),
|
||||
zap.Any("cursor", cursor),
|
||||
zap.Int64("limit", limit),
|
||||
zap.String("access_type", accessType))
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/harddelete.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// HardDeleteCollectionUseCase permanently deletes a collection
|
||||
// Used for GDPR right-to-be-forgotten implementation
|
||||
type HardDeleteCollectionUseCase interface {
|
||||
Execute(ctx context.Context, id gocql.UUID) error
|
||||
}
|
||||
|
||||
type hardDeleteCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewHardDeleteCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) HardDeleteCollectionUseCase {
|
||||
logger = logger.Named("HardDeleteCollectionUseCase")
|
||||
return &hardDeleteCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *hardDeleteCollectionUseCaseImpl) Execute(ctx context.Context, id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection hard deletion",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Hard delete from database (no tombstone).
|
||||
//
|
||||
|
||||
uc.logger.Info("Hard deleting collection (GDPR mode)",
|
||||
zap.String("collection_id", id.String()))
|
||||
|
||||
err := uc.repo.HardDelete(ctx, id)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to hard delete collection",
|
||||
zap.String("collection_id", id.String()),
|
||||
zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
uc.logger.Info("✅ Collection hard deleted successfully",
|
||||
zap.String("collection_id", id.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package collection
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
)
|
||||
|
||||
// NOTE: Unit tests for HardDeleteCollectionUseCase would require mocks.
|
||||
// For now, this use case will be tested via integration tests.
|
||||
// See Task 1.10 in RIGHT_TO_BE_FORGOTTEN_IMPLEMENTATION.md
|
||||
|
||||
func TestHardDeleteCollectionUseCase_Constructor(t *testing.T) {
|
||||
// Test that constructor creates use case successfully
|
||||
cfg := &config.Configuration{}
|
||||
logger := zap.NewNop()
|
||||
|
||||
useCase := NewHardDeleteCollectionUseCase(cfg, logger, nil)
|
||||
|
||||
if useCase == nil {
|
||||
t.Error("Expected use case to be created, got nil")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/list_by_user.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type ListCollectionsByUserUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) ([]*dom_collection.Collection, error)
|
||||
}
|
||||
|
||||
type listCollectionsByUserUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewListCollectionsByUserUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) ListCollectionsByUserUseCase {
|
||||
logger = logger.Named("ListCollectionsByUserUseCase")
|
||||
return &listCollectionsByUserUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *listCollectionsByUserUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) ([]*dom_collection.Collection, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating list collections by user",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
return uc.repo.GetAllByUserID(ctx, userID)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/list_shared_with_user.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type ListCollectionsSharedWithUserUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) ([]*dom_collection.Collection, error)
|
||||
}
|
||||
|
||||
type listCollectionsSharedWithUserUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewListCollectionsSharedWithUserUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) ListCollectionsSharedWithUserUseCase {
|
||||
logger = logger.Named("ListCollectionsSharedWithUserUseCase")
|
||||
return &listCollectionsSharedWithUserUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *listCollectionsSharedWithUserUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) ([]*dom_collection.Collection, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating list shared collections",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
return uc.repo.GetCollectionsSharedWithUser(ctx, userID)
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/move_collection.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// MoveCollectionRequest contains data needed to move a collection
|
||||
type MoveCollectionRequest struct {
|
||||
CollectionID gocql.UUID `json:"collection_id"`
|
||||
NewParentID gocql.UUID `json:"new_parent_id"`
|
||||
UpdatedAncestors []gocql.UUID `json:"updated_ancestors"`
|
||||
UpdatedPathSegments []string `json:"updated_path_segments"`
|
||||
}
|
||||
|
||||
type MoveCollectionUseCase interface {
|
||||
Execute(ctx context.Context, request MoveCollectionRequest) error
|
||||
}
|
||||
|
||||
type moveCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewMoveCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) MoveCollectionUseCase {
|
||||
logger = logger.Named("MoveCollectionUseCase")
|
||||
return &moveCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *moveCollectionUseCaseImpl) Execute(ctx context.Context, request MoveCollectionRequest) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if request.CollectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if request.NewParentID.String() == "" {
|
||||
e["new_parent_id"] = "New parent ID is required"
|
||||
}
|
||||
if len(request.UpdatedAncestors) == 0 {
|
||||
e["updated_ancestors"] = "Updated ancestors are required"
|
||||
}
|
||||
if len(request.UpdatedPathSegments) == 0 {
|
||||
e["updated_path_segments"] = "Updated path segments are required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating move collection",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Move collection.
|
||||
//
|
||||
|
||||
return uc.repo.MoveCollection(
|
||||
ctx,
|
||||
request.CollectionID,
|
||||
request.NewParentID,
|
||||
request.UpdatedAncestors,
|
||||
request.UpdatedPathSegments,
|
||||
)
|
||||
}
|
||||
216
cloud/maplefile-backend/internal/usecase/collection/provider.go
Normal file
216
cloud/maplefile-backend/internal/usecase/collection/provider.go
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
package collection
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
)
|
||||
|
||||
// Wire providers for collection use cases
|
||||
|
||||
func ProvideCreateCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CreateCollectionUseCase {
|
||||
return NewCreateCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideUpdateCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) UpdateCollectionUseCase {
|
||||
return NewUpdateCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) GetCollectionUseCase {
|
||||
return NewGetCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideSoftDeleteCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) SoftDeleteCollectionUseCase {
|
||||
return NewSoftDeleteCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideArchiveCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) ArchiveCollectionUseCase {
|
||||
return NewArchiveCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideRestoreCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RestoreCollectionUseCase {
|
||||
return NewRestoreCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideListCollectionsByUserUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) ListCollectionsByUserUseCase {
|
||||
return NewListCollectionsByUserUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideListCollectionsSharedWithUserUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) ListCollectionsSharedWithUserUseCase {
|
||||
return NewListCollectionsSharedWithUserUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideFindRootCollectionsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) FindRootCollectionsUseCase {
|
||||
return NewFindRootCollectionsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideFindCollectionsByParentUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) FindCollectionsByParentUseCase {
|
||||
return NewFindCollectionsByParentUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetCollectionSyncDataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) GetCollectionSyncDataUseCase {
|
||||
return NewGetCollectionSyncDataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideCheckCollectionAccessUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CheckCollectionAccessUseCase {
|
||||
return NewCheckCollectionAccessUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideCountUserCollectionsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CountUserCollectionsUseCase {
|
||||
return NewCountUserCollectionsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideCountUserFoldersUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) CountUserFoldersUseCase {
|
||||
return NewCountUserFoldersUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideUpdateMemberPermissionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) UpdateMemberPermissionUseCase {
|
||||
return NewUpdateMemberPermissionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideMoveCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) MoveCollectionUseCase {
|
||||
return NewMoveCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetFilteredCollectionsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) GetFilteredCollectionsUseCase {
|
||||
return NewGetFilteredCollectionsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideAddMemberToHierarchyUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AddMemberToHierarchyUseCase {
|
||||
return NewAddMemberToHierarchyUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideRemoveMemberFromHierarchyUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RemoveMemberFromHierarchyUseCase {
|
||||
return NewRemoveMemberFromHierarchyUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideFindDescendantsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) FindDescendantsUseCase {
|
||||
return NewFindDescendantsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideAddCollectionMemberUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AddCollectionMemberUseCase {
|
||||
return NewAddCollectionMemberUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideRemoveCollectionMemberUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RemoveCollectionMemberUseCase {
|
||||
return NewRemoveCollectionMemberUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideAnonymizeOldIPsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AnonymizeOldIPsUseCase {
|
||||
return NewAnonymizeOldIPsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideRemoveUserFromAllCollectionsUseCase(
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RemoveUserFromAllCollectionsUseCase {
|
||||
return NewRemoveUserFromAllCollectionsUseCase(logger, repo)
|
||||
}
|
||||
|
||||
func ProvideHardDeleteCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) HardDeleteCollectionUseCase {
|
||||
return NewHardDeleteCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideAnonymizeUserReferencesUseCase(
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) AnonymizeUserReferencesUseCase {
|
||||
return NewAnonymizeUserReferencesUseCase(logger, repo)
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/remove_member.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type RemoveCollectionMemberUseCase interface {
|
||||
Execute(ctx context.Context, collectionID, recipientID gocql.UUID) error
|
||||
}
|
||||
|
||||
type removeCollectionMemberUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewRemoveCollectionMemberUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RemoveCollectionMemberUseCase {
|
||||
logger = logger.Named("RemoveCollectionMemberUseCase")
|
||||
return &removeCollectionMemberUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *removeCollectionMemberUseCaseImpl) Execute(ctx context.Context, collectionID, recipientID gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if recipientID.String() == "" {
|
||||
e["recipient_id"] = "Recipient ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating remove collection member",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Remove member from collection.
|
||||
//
|
||||
|
||||
return uc.repo.RemoveMember(ctx, collectionID, recipientID)
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/remove_member_from_hierarchy.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type RemoveMemberFromHierarchyUseCase interface {
|
||||
Execute(ctx context.Context, rootID, recipientID gocql.UUID) error
|
||||
}
|
||||
|
||||
type removeMemberFromHierarchyUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewRemoveMemberFromHierarchyUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RemoveMemberFromHierarchyUseCase {
|
||||
logger = logger.Named("RemoveMemberFromHierarchyUseCase")
|
||||
return &removeMemberFromHierarchyUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *removeMemberFromHierarchyUseCaseImpl) Execute(ctx context.Context, rootID, recipientID gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if rootID.String() == "" {
|
||||
e["root_id"] = "Root collection ID is required"
|
||||
}
|
||||
if recipientID.String() == "" {
|
||||
e["recipient_id"] = "Recipient ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating remove member from hierarchy",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Remove member from collection hierarchy.
|
||||
//
|
||||
|
||||
return uc.repo.RemoveMemberFromHierarchy(ctx, rootID, recipientID)
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
)
|
||||
|
||||
// RemoveUserFromAllCollectionsUseCase removes a user from all collections they are a member of
|
||||
// Used for GDPR right-to-be-forgotten implementation
|
||||
type RemoveUserFromAllCollectionsUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID, userEmail string) (int, error)
|
||||
}
|
||||
|
||||
type removeUserFromAllCollectionsUseCaseImpl struct {
|
||||
logger *zap.Logger
|
||||
repo collection.CollectionRepository
|
||||
}
|
||||
|
||||
// NewRemoveUserFromAllCollectionsUseCase creates a new use case for removing user from all collections
|
||||
func NewRemoveUserFromAllCollectionsUseCase(
|
||||
logger *zap.Logger,
|
||||
repo collection.CollectionRepository,
|
||||
) RemoveUserFromAllCollectionsUseCase {
|
||||
return &removeUserFromAllCollectionsUseCaseImpl{
|
||||
logger: logger.Named("RemoveUserFromAllCollectionsUseCase"),
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (uc *removeUserFromAllCollectionsUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID, userEmail string) (int, error) {
|
||||
uc.logger.Info("Removing user from all shared collections",
|
||||
zap.String("user_id", userID.String()))
|
||||
|
||||
modifiedCollections, err := uc.repo.RemoveUserFromAllCollections(ctx, userID, userEmail)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to remove user from all collections",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return 0, err
|
||||
}
|
||||
|
||||
count := len(modifiedCollections)
|
||||
|
||||
uc.logger.Info("✅ Successfully removed user from all shared collections",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("collections_modified", count))
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package collection
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// NOTE: Unit tests for RemoveUserFromAllCollectionsUseCase would require mocks.
|
||||
// For now, this use case will be tested via integration tests.
|
||||
// See Task 1.10 in RIGHT_TO_BE_FORGOTTEN_IMPLEMENTATION.md
|
||||
|
||||
func TestRemoveUserFromAllCollectionsUseCase_Constructor(t *testing.T) {
|
||||
// Test that constructor creates use case successfully
|
||||
logger := zap.NewNop()
|
||||
|
||||
useCase := NewRemoveUserFromAllCollectionsUseCase(logger, nil)
|
||||
|
||||
if useCase == nil {
|
||||
t.Error("Expected use case to be created, got nil")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/collection/restore.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type RestoreCollectionUseCase interface {
|
||||
Execute(ctx context.Context, id gocql.UUID) error
|
||||
}
|
||||
|
||||
type restoreCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewRestoreCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) RestoreCollectionUseCase {
|
||||
logger = logger.Named("RestoreCollectionUseCase")
|
||||
return &restoreCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *restoreCollectionUseCaseImpl) Execute(ctx context.Context, id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection restoration",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Restore collection using repository method.
|
||||
//
|
||||
|
||||
return uc.repo.Restore(ctx, id)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/softdelete.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type SoftDeleteCollectionUseCase interface {
|
||||
Execute(ctx context.Context, id gocql.UUID) error
|
||||
}
|
||||
|
||||
type softDeleteCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewSoftDeleteCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) SoftDeleteCollectionUseCase {
|
||||
logger = logger.Named("SoftDeleteCollectionUseCase")
|
||||
return &softDeleteCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *softDeleteCollectionUseCaseImpl) Execute(ctx context.Context, id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection deletion",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Delete from database.
|
||||
//
|
||||
|
||||
return uc.repo.SoftDelete(ctx, id)
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/update.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type UpdateCollectionUseCase interface {
|
||||
Execute(ctx context.Context, collection *dom_collection.Collection) error
|
||||
}
|
||||
|
||||
type updateCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewUpdateCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) UpdateCollectionUseCase {
|
||||
logger = logger.Named("UpdateCollectionUseCase")
|
||||
return &updateCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *updateCollectionUseCaseImpl) Execute(ctx context.Context, collection *dom_collection.Collection) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collection == nil {
|
||||
e["collection"] = "Collection is required"
|
||||
} else {
|
||||
if collection.ID.String() == "" {
|
||||
e["id"] = "Collection ID is required"
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating collection update",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Update in database.
|
||||
//
|
||||
|
||||
return uc.repo.Update(ctx, collection)
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/collection/update_member_permission.go
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type UpdateMemberPermissionUseCase interface {
|
||||
Execute(ctx context.Context, collectionID, recipientID gocql.UUID, newPermission string) error
|
||||
}
|
||||
|
||||
type updateMemberPermissionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewUpdateMemberPermissionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_collection.CollectionRepository,
|
||||
) UpdateMemberPermissionUseCase {
|
||||
logger = logger.Named("UpdateMemberPermissionUseCase")
|
||||
return &updateMemberPermissionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *updateMemberPermissionUseCaseImpl) Execute(ctx context.Context, collectionID, recipientID gocql.UUID, newPermission string) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if recipientID.String() == "" {
|
||||
e["recipient_id"] = "Recipient ID is required"
|
||||
}
|
||||
if newPermission == "" {
|
||||
// Default permission level will be set in the repository
|
||||
} else if newPermission != dom_collection.CollectionPermissionReadOnly &&
|
||||
newPermission != dom_collection.CollectionPermissionReadWrite &&
|
||||
newPermission != dom_collection.CollectionPermissionAdmin {
|
||||
e["permission_level"] = "Invalid permission level"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating update member permission",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Update member permission.
|
||||
//
|
||||
|
||||
return uc.repo.UpdateMemberPermission(ctx, collectionID, recipientID, newPermission)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue