diff --git a/cmd/server/file_test.go b/cmd/server/file_test.go index 1b62027..2d2d9fb 100644 --- a/cmd/server/file_test.go +++ b/cmd/server/file_test.go @@ -185,7 +185,7 @@ func setupFileTestRouter(t *testing.T) (*gin.Engine, *gorm.DB, *inMemoryStorage) uploadSvc := service.NewUploadService(memStore, blobStore, fileStore, uploadStore, cfg, db, zap.NewNop()) _ = service.NewDownloadService(memStore, blobStore, fileStore, "files", zap.NewNop()) folderSvc := service.NewFolderService(folderStore, fileStore, zap.NewNop()) - fileSvc := service.NewFileService(memStore, blobStore, fileStore, "files", db, zap.NewNop()) + fileSvc := service.NewFileService(memStore, blobStore, fileStore, folderStore, "files", db, zap.NewNop()) uploadH := handler.NewUploadHandler(uploadSvc, zap.NewNop()) fileH := handler.NewFileHandler(fileSvc, zap.NewNop()) diff --git a/internal/app/app.go b/internal/app/app.go index 785b6b9..7c14137 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -188,7 +188,7 @@ func initHTTPServer(cfg *config.Config, db *gorm.DB, slurmClient *slurm.Client, uploadSvc := service.NewUploadService(minioClient, blobStore, fileStore, uploadStore, cfg.Minio, db, logger) folderSvc := service.NewFolderService(folderStore, fileStore, logger) - fileSvc := service.NewFileService(minioClient, blobStore, fileStore, cfg.Minio.Bucket, db, logger) + fileSvc := service.NewFileService(minioClient, blobStore, fileStore, folderStore, cfg.Minio.Bucket, db, logger) uploadH = handler.NewUploadHandler(uploadSvc, logger) fileH = handler.NewFileHandler(fileSvc, logger) diff --git a/internal/handler/file_handler.go b/internal/handler/file_handler.go index ec49f3f..6167a8c 100644 --- a/internal/handler/file_handler.go +++ b/internal/handler/file_handler.go @@ -14,7 +14,8 @@ import ( ) type fileServiceProvider interface { - ListFiles(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) + ListFiles(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) + GetFileResponse(ctx context.Context, fileID int64) (*model.FileResponse, error) GetFileMetadata(ctx context.Context, fileID int64) (*model.File, *model.FileBlob, error) DownloadFile(ctx context.Context, fileID int64, rangeHeader string) (io.ReadCloser, *model.File, *model.FileBlob, int64, int64, error) DeleteFile(ctx context.Context, fileID int64) error @@ -50,9 +51,20 @@ func (h *FileHandler) ListFiles(c *gin.Context) { folderID = &id } + var userID *int64 + if v := c.Query("user_id"); v != "" { + id, err := strconv.ParseInt(v, 10, 64) + if err != nil { + h.logger.Warn("invalid user_id", zap.String("user_id", v)) + server.BadRequest(c, "invalid user_id") + return + } + userID = &id + } + search := c.Query("search") - files, total, err := h.svc.ListFiles(c.Request.Context(), folderID, page, pageSize, search) + files, total, err := h.svc.ListFiles(c.Request.Context(), folderID, userID, page, pageSize, search) if err != nil { h.logger.Error("failed to list files", zap.Error(err)) server.InternalError(c, err.Error()) @@ -75,23 +87,13 @@ func (h *FileHandler) GetFile(c *gin.Context) { return } - file, blob, err := h.svc.GetFileMetadata(c.Request.Context(), id) + resp, err := h.svc.GetFileResponse(c.Request.Context(), id) if err != nil { h.logger.Error("failed to get file", zap.Int64("id", id), zap.Error(err)) server.InternalError(c, err.Error()) return } - resp := model.FileResponse{ - ID: file.ID, - Name: file.Name, - FolderID: file.FolderID, - Size: blob.FileSize, - MimeType: blob.MimeType, - SHA256: file.BlobSHA256, - CreatedAt: file.CreatedAt, - UpdatedAt: file.UpdatedAt, - } server.OK(c, resp) } diff --git a/internal/handler/file_handler_test.go b/internal/handler/file_handler_test.go index d8d0f70..7b32d43 100644 --- a/internal/handler/file_handler_test.go +++ b/internal/handler/file_handler_test.go @@ -18,14 +18,19 @@ import ( ) type mockFileService struct { - listFilesFn func(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) + listFilesFn func(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) + getFileResponseFn func(ctx context.Context, fileID int64) (*model.FileResponse, error) getFileMetadataFn func(ctx context.Context, fileID int64) (*model.File, *model.FileBlob, error) downloadFileFn func(ctx context.Context, fileID int64, rangeHeader string) (io.ReadCloser, *model.File, *model.FileBlob, int64, int64, error) deleteFileFn func(ctx context.Context, fileID int64) error } -func (m *mockFileService) ListFiles(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { - return m.listFilesFn(ctx, folderID, page, pageSize, search) +func (m *mockFileService) ListFiles(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { + return m.listFilesFn(ctx, folderID, userID, page, pageSize, search) +} + +func (m *mockFileService) GetFileResponse(ctx context.Context, fileID int64) (*model.FileResponse, error) { + return m.getFileResponseFn(ctx, fileID) } func (m *mockFileService) GetFileMetadata(ctx context.Context, fileID int64) (*model.File, *model.FileBlob, error) { @@ -67,7 +72,7 @@ func newFileHandlerSetup() *fileHandlerSetup { func TestListFiles_Empty(t *testing.T) { s := newFileHandlerSetup() - s.mock.listFilesFn = func(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { + s.mock.listFilesFn = func(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { return []model.FileResponse{}, 0, nil } @@ -97,7 +102,7 @@ func TestListFiles_Empty(t *testing.T) { func TestListFiles_WithFiles(t *testing.T) { s := newFileHandlerSetup() now := time.Now() - s.mock.listFilesFn = func(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { + s.mock.listFilesFn = func(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { return []model.FileResponse{ {ID: 1, Name: "a.txt", Size: 100, MimeType: "text/plain", SHA256: "abc123", CreatedAt: now, UpdatedAt: now}, {ID: 2, Name: "b.pdf", Size: 200, MimeType: "application/pdf", SHA256: "def456", CreatedAt: now, UpdatedAt: now}, @@ -133,7 +138,7 @@ func TestListFiles_WithFiles(t *testing.T) { func TestListFiles_WithFolderID(t *testing.T) { s := newFileHandlerSetup() var capturedFolderID *int64 - s.mock.listFilesFn = func(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { + s.mock.listFilesFn = func(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { capturedFolderID = folderID return []model.FileResponse{}, 0, nil } @@ -152,7 +157,7 @@ func TestListFiles_WithFolderID(t *testing.T) { func TestListFiles_ServiceError(t *testing.T) { s := newFileHandlerSetup() - s.mock.listFilesFn = func(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { + s.mock.listFilesFn = func(ctx context.Context, folderID *int64, userID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) { return nil, 0, fmt.Errorf("db error") } @@ -170,12 +175,11 @@ func TestListFiles_ServiceError(t *testing.T) { func TestGetFile_Found(t *testing.T) { s := newFileHandlerSetup() now := time.Now() - s.mock.getFileMetadataFn = func(ctx context.Context, fileID int64) (*model.File, *model.FileBlob, error) { - return &model.File{ - ID: 1, Name: "test.txt", BlobSHA256: "abc123", CreatedAt: now, UpdatedAt: now, - }, &model.FileBlob{ - ID: 1, SHA256: "abc123", FileSize: 1024, MimeType: "text/plain", CreatedAt: now, - }, nil + rootPath := "/" + s.mock.getFileResponseFn = func(ctx context.Context, fileID int64) (*model.FileResponse, error) { + return &model.FileResponse{ + ID: 1, Name: "test.txt", SHA256: "abc123", FolderPath: &rootPath, CreatedAt: now, UpdatedAt: now, + }, nil } w := httptest.NewRecorder() @@ -195,8 +199,8 @@ func TestGetFile_Found(t *testing.T) { func TestGetFile_NotFound(t *testing.T) { s := newFileHandlerSetup() - s.mock.getFileMetadataFn = func(ctx context.Context, fileID int64) (*model.File, *model.FileBlob, error) { - return nil, nil, fmt.Errorf("file not found: 999") + s.mock.getFileResponseFn = func(ctx context.Context, fileID int64) (*model.FileResponse, error) { + return nil, fmt.Errorf("file not found: 999") } w := httptest.NewRecorder() diff --git a/internal/testutil/testenv/env.go b/internal/testutil/testenv/env.go index 4a231a3..bd0337c 100644 --- a/internal/testutil/testenv/env.go +++ b/internal/testutil/testenv/env.go @@ -180,7 +180,7 @@ func NewTestEnv(t interface { taskSvc := service.NewTaskService(taskStore, appStore, fileStore, blobStore, stagingSvc, jobSvc, workDir, logger) appSvc := service.NewApplicationService(appStore, jobSvc, workDir, logger, taskSvc) uploadSvc := service.NewUploadService(mockMinIO, blobStore, fileStore, uploadStore, minioCfg, db, logger) - fileSvc := service.NewFileService(mockMinIO, blobStore, fileStore, minioCfg.Bucket, db, logger) + fileSvc := service.NewFileService(mockMinIO, blobStore, fileStore, folderStore, minioCfg.Bucket, db, logger) // 9. All 7 Handler instances jobH := handler.NewJobHandler(jobSvc, logger)