// monorepo/cloud/backend/internal/maplefile/service/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" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/constants" dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror" ) type FindByParentRequestDTO struct { ParentID gocql.UUID `json:"parent_id"` } type FindCollectionsByParentService interface { Execute(ctx context.Context, req *FindByParentRequestDTO) (*CollectionsResponseDTO, error) } type findCollectionsByParentServiceImpl struct { config *config.Configuration logger *zap.Logger repo dom_collection.CollectionRepository } func NewFindCollectionsByParentService( config *config.Configuration, logger *zap.Logger, repo dom_collection.CollectionRepository, ) FindCollectionsByParentService { logger = logger.Named("FindCollectionsByParentService") return &findCollectionsByParentServiceImpl{ config: config, logger: logger, repo: repo, } } func (svc *findCollectionsByParentServiceImpl) Execute(ctx context.Context, req *FindByParentRequestDTO) (*CollectionsResponseDTO, error) { // // STEP 1: Validation // if req == nil { svc.logger.Warn("Failed validation with nil request") return nil, httperror.NewForBadRequestWithSingleField("non_field_error", "Parent ID is required") } if req.ParentID.String() == "" { svc.logger.Warn("Empty parent ID provided") return nil, httperror.NewForBadRequestWithSingleField("parent_id", "Parent ID is required") } // // STEP 2: Get user ID from context // userID, ok := ctx.Value(constants.SessionUserID).(gocql.UUID) if !ok { svc.logger.Error("Failed getting user ID from context") return nil, httperror.NewForInternalServerErrorWithSingleField("message", "Authentication context error") } // // STEP 3: Check if user has access to the parent collection // hasAccess, err := svc.repo.CheckAccess(ctx, req.ParentID, userID, dom_collection.CollectionPermissionReadOnly) if err != nil { svc.logger.Error("Failed to check access", zap.Any("error", err), zap.Any("parent_id", req.ParentID), zap.Any("user_id", userID)) return nil, err } if !hasAccess { svc.logger.Warn("Unauthorized parent collection access attempt", zap.Any("user_id", userID), zap.Any("parent_id", req.ParentID)) return nil, httperror.NewForForbiddenWithSingleField("message", "You don't have access to this parent collection") } // // STEP 4: Find collections by parent // collections, err := svc.repo.FindByParent(ctx, req.ParentID) if err != nil { svc.logger.Error("Failed to find collections by parent", zap.Any("error", err), zap.Any("parent_id", req.ParentID)) return nil, err } // // STEP 5: Map domain models to response DTOs // response := &CollectionsResponseDTO{ Collections: make([]*CollectionResponseDTO, len(collections)), } for i, collection := range collections { ownerEmail := getOwnerEmailFromMembers(collection) response.Collections[i] = mapCollectionToDTO(collection, 0, ownerEmail) } svc.logger.Debug("Found collections by parent", zap.Int("count", len(collections)), zap.Any("parent_id", req.ParentID)) return response, nil }