Skip to content

refactor(api): use Path for FileUtils.getBookFullPath#209

Merged
balazs-szucs merged 2 commits intogrimmory-tools:developfrom
imnotjames:refactor/29/use-path-for-get-book-full-path
Mar 27, 2026
Merged

refactor(api): use Path for FileUtils.getBookFullPath#209
balazs-szucs merged 2 commits intogrimmory-tools:developfrom
imnotjames:refactor/29/use-path-for-get-book-full-path

Conversation

@imnotjames
Copy link
Copy Markdown
Contributor

@imnotjames imnotjames commented Mar 26, 2026

Description

This change passes around a Path object instead of a string in FileUtils.getBookFullPath(). In getBookFullPath we convert the path to a string, only to (in most cases) convert right back to a path. The conversion opens up room for error, and it's a heck of a lot simpler to just pass the path back and forth.

For example, even in FileUtils.getBookFullPath, we convert backslashes and then in other locations, we ALSO have to convert backslashes. Path handles these under the hood natively.

This was originally part of #113 but I split it out because it seems beneficial on its own & helps with testing and reviewing the nightcompress library.

Linked Issue: Related to #29

Changes

  • updates FileUtils.getBookFullPath() to return a java.nio.file.Path instead of a String
  • refactors other code paths to use this Path

Summary by CodeRabbit

  • Refactor
    • Standardized internal file-path handling across downloads, streaming, cover extraction, metadata retrieval, and email attachments to improve consistency and reduce path-related errors.
  • Tests
    • Updated test fixtures and mocks to use the new file-path representation, keeping tests aligned with runtime behavior and validating the refactor.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

FileUtils.getBookFullPath(...) now returns java.nio.file.Path; callers were updated to consume Path directly or convert via .toFile() / .toString() where needed. Tests and static mocks were updated to return and assert Path values.

Changes

Cohort / File(s) Summary
FileUtils API Changes
booklore-api/src/main/java/org/booklore/util/FileUtils.java
Changed getBookFullPath(BookEntity) and getBookFullPath(BookEntity, BookFileEntity) return types from String to Path; removed string-based normalization and updated usages (e.g., file size calculation).
Service callers & downloads
booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java, booklore-api/src/main/java/org/booklore/service/book/BookService.java, booklore-api/src/main/java/org/booklore/service/email/SendEmailV2Service.java, booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java
Replaced string-path handling and redundant Paths.get(...) with direct Path usage; added .toFile() or .toString() where downstream APIs require File or String.
File processors
booklore-api/src/main/java/org/booklore/service/fileprocessor/...
Azw3Processor.java, EpubProcessor.java, Fb2Processor.java, MobiProcessor.java, PdfProcessor.java, CbxProcessor.java, AudiobookProcessor.java
Cover extraction and metadata routines now obtain java.io.File via FileUtils.getBookFullPath(...).toFile() instead of constructing File from a String; minor import adjustments.
Reader services
booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java, .../EpubReaderService.java, .../PdfReaderService.java
Simplified getBookPath null-type branches to return the Path from FileUtils.getBookFullPath() directly, removing intermediate String→Path conversions.
Email & download stream callers
booklore-api/src/main/java/org/booklore/service/book/BookService.java, .../BookDownloadService.java, .../SendEmailV2Service.java
Where code previously wrapped returned path strings in new File(...) or Paths.get(...), callers now use the returned Path and call .toFile() or .toString() as appropriate.
Tests / Mocks
booklore-api/src/test/java/org/booklore/...
service/book/BookServiceTest.java, service/email/SendEmailV2ServiceTest.java, service/reader/CbxReaderServiceTest.java, service/reader/EpubReaderServiceTest.java, service/Rar5FallbackIntegrationTest.java, util/FileUtilsTest.java
Updated tests and static mocks to return and assert Path instances (instead of String) for FileUtils.getBookFullPath(); adjusted fixtures and verifications to use Path values.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • imajes
  • balazs-szucs

Poem

🐰 I hopped from String to Path with glee,
Files now wear shoes of java.nio,
.toFile() crumbs neatly swept away,
Tests woke up and joined the play,
A rabbit's nibble — tidy tree! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title follows conventional commit format with type 'refactor' and clear, descriptive scope about the FileUtils.getBookFullPath change.
Description check ✅ Passed The PR description includes all required sections: a clear explanation of what the change does and why, a 'Linked Issue' reference, and itemized 'Changes' list.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands and usage tips.

@imnotjames imnotjames marked this pull request as ready for review March 26, 2026 02:42
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java (1)

478-485: ⚠️ Potential issue | 🔴 Critical

Fix stale String stub for getBookFullPath in streamBookContent_delegatesToStreamingService.

The method FileUtils.getBookFullPath(BookEntity) returns Path, not String. The current stub returns a String literal which is type-unsafe and inconsistent with other stubs in this test file (lines 128, 156, 332, 353 all correctly use Path.of(...)). This will cause type mismatch in Mockito stubbing.

Suggested patch
         try (MockedStatic<FileUtils> fileUtilsMock = mockStatic(FileUtils.class)) {
-            fileUtilsMock.when(() -> FileUtils.getBookFullPath(entity)).thenReturn("/tmp/library/book.epub");
+            Path fullPath = Path.of("/tmp/library/book.epub");
+            fileUtilsMock.when(() -> FileUtils.getBookFullPath(entity)).thenReturn(fullPath);

             bookService.streamBookContent(15L, null, request, response);

             verify(bookRepository).findByIdWithBookFiles(15L);
-            verify(fileStreamingService).streamWithRangeSupport(eq(Paths.get("/tmp/library/book.epub")), eq("application/epub+zip"), eq(request), eq(response));
+            verify(fileStreamingService).streamWithRangeSupport(eq(fullPath), eq("application/epub+zip"), eq(request), eq(response));
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java`
around lines 478 - 485, The stub for FileUtils.getBookFullPath in the test
streamBookContent_delegatesToStreamingService is returning a String but the
method returns a Path; update the mock to return a Path (use Path.of(...) or
Paths.get(...)) instead of "/tmp/library/book.epub" so the mocked static
FileUtils.getBookFullPath(entity) matches the expected return type and the
verify call to fileStreamingService.streamWithRangeSupport still uses
Paths.get("/tmp/library/book.epub") for comparison.
🧹 Nitpick comments (1)
booklore-api/src/main/java/org/booklore/service/book/BookService.java (1)

341-341: Prefer keeping filePath as Path end-to-end in both methods.

Current flow converts Path -> String -> File/Path. Keeping Path throughout is cleaner and better aligned with this PR’s objective.

Refactor sketch
-        String filePath;
+        Path filePath;
         if (bookType != null) {
             BookFileType requestedType = BookFileType.valueOf(bookType.toUpperCase());
             BookFileEntity bookFile = bookEntity.getBookFiles().stream()
                     .filter(bf -> bf.getBookType() == requestedType)
                     .findFirst()
                     .orElseThrow(() -> ApiError.FILE_NOT_FOUND.createException("No file of type " + bookType + " found for book"));
-            filePath = bookFile.getFullFilePath().toString();
+            filePath = bookFile.getFullFilePath();
         } else {
-            filePath = FileUtils.getBookFullPath(bookEntity).toString();
+            filePath = FileUtils.getBookFullPath(bookEntity);
         }
-        File file = new File(filePath);
+        File file = filePath.toFile();
         if (!file.exists()) {
-            throw ApiError.FILE_NOT_FOUND.createException(filePath);
+            throw ApiError.FILE_NOT_FOUND.createException(filePath.toString());
         }

@@
-        String filePath;
+        Path filePath;
         if (bookType != null) {
             BookFileType requestedType = BookFileType.valueOf(bookType.toUpperCase());
             BookFileEntity bookFile = bookEntity.getBookFiles().stream()
                     .filter(bf -> bf.getBookType() == requestedType)
                     .findFirst()
                     .orElseThrow(() -> ApiError.FILE_NOT_FOUND.createException("No file of type " + bookType + " found for book"));
-            filePath = bookFile.getFullFilePath().toString();
+            filePath = bookFile.getFullFilePath();
         } else {
-            filePath = FileUtils.getBookFullPath(bookEntity).toString();
+            filePath = FileUtils.getBookFullPath(bookEntity);
         }
-
-        Path path = Paths.get(filePath);
+        Path path = filePath;

Also applies to: 365-365

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/service/book/BookService.java` at
line 341, The code converts the Path returned by
FileUtils.getBookFullPath(bookEntity) to a String (filePath) and back to
File/Path later; change filePath to be a Path (e.g., Path filePath =
FileUtils.getBookFullPath(bookEntity)) and propagate Path usage end-to-end in
the surrounding methods instead of calling toString(), updating any downstream
calls that currently accept String to accept Path (or perform a single,
localized conversion at the I/O boundary such as filePath.toFile() only where
APIs require File); adjust method signatures or call sites as needed so both
occurrences that use FileUtils.getBookFullPath(bookEntity) (the current line and
the other at the later occurrence) keep a Path rather than a String.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java`:
- Around line 32-38: The method FileUtils.getBookFullPath must guard against
nullable path parts: check bookFile.getFileSubPath() for null before calling
Path.of(...) and supply an empty string or omit that segment when building the
Path, and ensure you return null or Optional<Path> consistently; likewise,
callers that call Files.exists(...) on getBookFullPath() (e.g., the code around
lines 53-55) must first check that getBookFullPath(...) did not return null
before calling Files.exists. Update FileUtils.getBookFullPath to handle null
bookFile.getFileSubPath (and null bookEntity.getLibraryPath) safely and update
any Files.exists(...) call sites to null-check the returned Path (or change the
method to return Optional<Path> and adapt callers).

---

Outside diff comments:
In `@booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java`:
- Around line 478-485: The stub for FileUtils.getBookFullPath in the test
streamBookContent_delegatesToStreamingService is returning a String but the
method returns a Path; update the mock to return a Path (use Path.of(...) or
Paths.get(...)) instead of "/tmp/library/book.epub" so the mocked static
FileUtils.getBookFullPath(entity) matches the expected return type and the
verify call to fileStreamingService.streamWithRangeSupport still uses
Paths.get("/tmp/library/book.epub") for comparison.

---

Nitpick comments:
In `@booklore-api/src/main/java/org/booklore/service/book/BookService.java`:
- Line 341: The code converts the Path returned by
FileUtils.getBookFullPath(bookEntity) to a String (filePath) and back to
File/Path later; change filePath to be a Path (e.g., Path filePath =
FileUtils.getBookFullPath(bookEntity)) and propagate Path usage end-to-end in
the surrounding methods instead of calling toString(), updating any downstream
calls that currently accept String to accept Path (or perform a single,
localized conversion at the I/O boundary such as filePath.toFile() only where
APIs require File); adjust method signatures or call sites as needed so both
occurrences that use FileUtils.getBookFullPath(bookEntity) (the current line and
the other at the later occurrence) keep a Path rather than a String.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e33ab1e-bf36-483d-82ba-17ae35ea652c

📥 Commits

Reviewing files that changed from the base of the PR and between 75a4d2f and 015e8c9.

📒 Files selected for processing (21)
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/main/java/org/booklore/service/email/SendEmailV2Service.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AudiobookProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Azw3Processor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/EpubProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Fb2Processor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/MobiProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/PdfProcessor.java
  • booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java
  • booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java
  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/CbxReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Suite / Backend Tests
🧰 Additional context used
📓 Path-based instructions (2)
booklore-api/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

booklore-api/src/**/*.java: Use 4-space indentation in Java files and match surrounding Java style
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection
Use MapStruct for entity/DTO mapping

Files:

  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/MobiProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java
  • booklore-api/src/test/java/org/booklore/service/reader/CbxReaderServiceTest.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Fb2Processor.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/PdfProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Azw3Processor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AudiobookProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/EpubProcessor.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
  • booklore-api/src/main/java/org/booklore/service/email/SendEmailV2Service.java
  • booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
booklore-api/src/test/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

Prefer focused unit tests; use @SpringBootTest only when the Spring context is required

Files:

  • booklore-api/src/test/java/org/booklore/service/reader/CbxReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
🧠 Learnings (8)
📓 Common learnings
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 146
File: booklore-api/src/main/java/org/booklore/util/FileService.java:151-168
Timestamp: 2026-03-23T18:36:05.843Z
Learning: In `booklore-api/src/main/java/org/booklore/util/FileService.java`, the `"bin"` search path in `getSystemSearchPath()` is intentionally left as a relative path (not derived from `appProperties.getPathConfig()`), so binary lookup resolves relative to the process working directory. Do not flag this as an issue.
📚 Learning: 2026-03-23T18:36:05.843Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 146
File: booklore-api/src/main/java/org/booklore/util/FileService.java:151-168
Timestamp: 2026-03-23T18:36:05.843Z
Learning: In `booklore-api/src/main/java/org/booklore/util/FileService.java`, the `"bin"` search path in `getSystemSearchPath()` is intentionally left as a relative path (not derived from `appProperties.getPathConfig()`), so binary lookup resolves relative to the process working directory. Do not flag this as an issue.

Applied to files:

  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/PdfProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AudiobookProcessor.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
  • booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
📚 Learning: 2026-03-26T01:46:48.847Z
Learnt from: CR
Repo: grimmory-tools/grimmory PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T01:46:48.847Z
Learning: Applies to booklore-api/src/**/*Entity.java : Keep JPA entities on the `*Entity` suffix

Applied to files:

  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
📚 Learning: 2026-03-24T18:46:47.249Z
Learnt from: pedronave
Repo: grimmory-tools/grimmory PR: 173
File: booklore-api/src/main/java/org/booklore/service/hardcover/HardcoverSyncService.java:188-199
Timestamp: 2026-03-24T18:46:47.249Z
Learning: In grimmory-tools/grimmory (HardcoverSyncService.java), when a `hardcoverBookId` is stored in book metadata but is malformed (non-numeric), the preferred behavior is to return null and skip the sync rather than falling back to ISBN-based lookup. The reasoning is that an explicitly set bookId represents deliberate user intent, and silently resolving via ISBN could sync progress to a different book than intended.

Applied to files:

  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
📚 Learning: 2026-03-26T01:46:48.847Z
Learnt from: CR
Repo: grimmory-tools/grimmory PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T01:46:48.847Z
Learning: Applies to booklore-api/src/test/**/*.java : Prefer focused unit tests; use `SpringBootTest` only when the Spring context is required

Applied to files:

  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
📚 Learning: 2026-03-26T01:46:48.847Z
Learnt from: CR
Repo: grimmory-tools/grimmory PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T01:46:48.847Z
Learning: Applies to booklore-api/src/**/*.java : Use MapStruct for entity/DTO mapping

Applied to files:

  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
📚 Learning: 2026-03-26T01:46:48.847Z
Learnt from: CR
Repo: grimmory-tools/grimmory PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T01:46:48.847Z
Learning: Applies to booklore-api/src/**/*.java : Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce `Autowired` field injection

Applied to files:

  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java
📚 Learning: 2026-03-24T18:58:08.199Z
Learnt from: pedronave
Repo: grimmory-tools/grimmory PR: 173
File: booklore-api/src/main/java/org/booklore/service/hardcover/HardcoverSyncService.java:291-294
Timestamp: 2026-03-24T18:58:08.199Z
Learning: In grimmory-tools/grimmory (HardcoverSyncService.java), when a Hardcover edition has no page count, falling back to the book-level page count (book.pages) is the preferred behavior. Skipping the sync entirely in this case is considered worse than using a slightly approximate total, because the page count is only a proxy for converting progress % to page numbers for the API. The Hardcover API allows setting page-based progress even when the edition lacks a page count, and progress displays in at least some UI views (e.g., the book detail dropdown).

Applied to files:

  • booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java
🔇 Additional comments (20)
booklore-api/src/main/java/org/booklore/util/FileUtils.java (1)

27-30: Good delegation after return-type migration.

This keeps one path-construction source of truth and avoids duplicate conversion logic.

booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java (1)

147-147: Path propagation is correct here.

Directly returning FileUtils.getBookFullPath(bookEntity) is consistent with the refactor and removes unnecessary wrapping.

booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java (1)

116-116: Looks good for the Path migration.

This removes redundant conversion and keeps getBookPath(...) type-consistent.

booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java (1)

200-200: Correct Path -> File conversion at extractor boundaries.

Using .toFile() here is the right adaptation for the updated FileUtils API.

Also applies to: 210-210

booklore-api/src/main/java/org/booklore/service/fileprocessor/Fb2Processor.java (1)

74-74: Nice update for the new path contract.

.toFile() is the correct conversion point for the cover extractor input.

booklore-api/src/main/java/org/booklore/service/fileprocessor/AudiobookProcessor.java (1)

163-163: Good migration in the non-folder path branch.

This is the expected conversion for APIs that still operate on File.

booklore-api/src/main/java/org/booklore/service/fileprocessor/Azw3Processor.java (1)

75-75: This conversion change is correct.

Using .toFile() here cleanly matches the updated FileUtils return type.

booklore-api/src/main/java/org/booklore/service/fileprocessor/EpubProcessor.java (1)

76-76: Path migration is correctly applied in cover extraction.

Line 76 cleanly consumes the new Path return type and avoids redundant conversions.

booklore-api/src/main/java/org/booklore/service/email/SendEmailV2Service.java (1)

99-99: Attachment path handling is correctly updated to Path.

Line 99 correctly converts the returned Path to File at the API boundary that requires File.

booklore-api/src/main/java/org/booklore/service/fileprocessor/MobiProcessor.java (1)

75-75: Path to File conversion is correctly placed.

Line 75 applies the refactor cleanly with no extra normalization or string round-tripping.

booklore-api/src/main/java/org/booklore/service/fileprocessor/PdfProcessor.java (1)

77-77: PDF call sites are correctly updated for the new Path contract.

Lines 77 and 107 adapt to FileUtils.getBookFullPath(...) returning Path without introducing extra conversion steps.

Also applies to: 107-107

booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java (1)

69-69: CBX path refactor is consistently applied at all relevant call sites.

These updates correctly convert Path to File only where required by downstream APIs.

Also applies to: 92-92, 255-255

booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java (1)

62-62: Download path handling now correctly uses Path end-to-end.

Line 62 and Line 261 apply the refactor cleanly and remove unnecessary String/path conversion.

Also applies to: 261-261

booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java (1)

220-220: Direct Path return in getBookPath(...) is the right simplification.

Line 220 removes an unnecessary conversion layer and keeps return types consistent across both branches.

booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java (1)

113-113: Tests are correctly aligned with the Path return-type refactor.

All updated mocks now return Path, matching production behavior and keeping these unit tests accurate.

Also applies to: 152-152, 180-180, 215-215, 241-241, 267-267, 302-302, 328-328, 356-356, 378-378, 399-399, 427-427, 456-456, 497-497

booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java (1)

23-23: Path-based mock migration looks correct.

The updated import and static mock return values are consistent with the new FileUtils.getBookFullPath(...) Path contract.

Also applies to: 126-126, 196-196, 229-229

booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java (1)

77-77: Good update to Path return stubs.

Returning cbrCopy directly is the correct adaptation for the Path-returning FileUtils.getBookFullPath(...).

Also applies to: 99-99

booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java (1)

45-45: Test assertion is correctly migrated to Path semantics.

Using Path for both actual and expected values is the right change for this refactor.

Also applies to: 47-49

booklore-api/src/test/java/org/booklore/service/reader/CbxReaderServiceTest.java (1)

66-66: Consistent and correct Path-based stub updates across reader tests.

All affected FileUtils.getBookFullPath(...) mocks now match the new method return type.

Also applies to: 97-97, 144-144, 183-183, 212-212, 235-235, 264-264, 295-295, 308-308

booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java (1)

128-128: These migrated stubs are correct.

The updated FileUtils.getBookFullPath(...) mocks now correctly return Path in the touched tests.

Also applies to: 156-156, 332-332, 353-353

@imajes
Copy link
Copy Markdown
Member

imajes commented Mar 26, 2026

@imnotjames please make sure to align this with PR #82 -- we need to avoid merge conflicts if we can :) 👍

@imnotjames imnotjames marked this pull request as draft March 26, 2026 03:01
@imnotjames
Copy link
Copy Markdown
Contributor Author

@imnotjames please make sure to align this with PR #82 -- we need to avoid merge conflicts if we can :) 👍

Sure thing. I did a quick test merge and there's currently two conflicts we can't easily avoid -- at the FileUtils class itself - since we're changing methods very close to one another, and at

https://github.com/grimmory-tools/grimmory/pull/82/changes#diff-c442d87c5cfc4ab9f81942e178449a1572798cad67a397815ee2ba279e469ca7L62

Because the change moves us away from getBookFullPath() in that PR.

@imnotjames imnotjames marked this pull request as ready for review March 26, 2026 03:16
@imnotjames imnotjames force-pushed the refactor/29/use-path-for-get-book-full-path branch 2 times, most recently from 39100c8 to f6acfd3 Compare March 26, 2026 16:12
@imnotjames imnotjames force-pushed the refactor/29/use-path-for-get-book-full-path branch from f6acfd3 to b1ff4e1 Compare March 27, 2026 14:55
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
booklore-api/src/main/java/org/booklore/util/FileUtils.java (1)

27-38: Complete the Path refactoring in BookService to eliminate unnecessary String round-trips.

The FileUtils.getBookFullPath() refactor to return Path is correct, but BookService still converts these Path objects to String and reparses them back to File. This pattern appears at lines 339, 341, 363, and 365, leaving the cleanup incomplete.

♻️ Suggested refactor in BookService
-        String filePath;
+        Path filePath;
         if (bookType != null) {
             BookFileType requestedType = BookFileType.valueOf(bookType.toUpperCase());
             BookFileEntity bookFile = bookEntity.getBookFiles().stream()
                     .filter(bf -> bf.getBookType() == requestedType)
                     .findFirst()
                     .orElseThrow(() -> ApiError.FILE_NOT_FOUND.createException("No file of type " + bookType + " found for book"));
-            filePath = bookFile.getFullFilePath().toString();
+            filePath = bookFile.getFullFilePath();
         } else {
-            filePath = FileUtils.getBookFullPath(bookEntity).toString();
+            filePath = FileUtils.getBookFullPath(bookEntity);
         }
-        File file = new File(filePath);
+        File file = filePath.toFile();
         if (!file.exists()) {
-            throw ApiError.FILE_NOT_FOUND.createException(filePath);
+            throw ApiError.FILE_NOT_FOUND.createException(filePath.toString());
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java` around lines 27
- 38, BookService still converts Path results from
FileUtils.getBookFullPath(...) to String and back to File, undoing the Path
refactor; update the call sites (the usages around the previous lines 339, 341,
363, 365) to accept and work with java.nio.file.Path directly instead of doing
toString() and new File(...). Replace any File-constructing logic with
Path-based APIs (Files.exists, Files.delete, Paths.get.resolve, Path.resolve,
etc.) or use File.toPath()/Path.toFile() only when an API strictly requires
java.io.File, and update method signatures/variables in BookService accordingly
so no unnecessary String round-trips remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java`:
- Around line 27-38: BookService still converts Path results from
FileUtils.getBookFullPath(...) to String and back to File, undoing the Path
refactor; update the call sites (the usages around the previous lines 339, 341,
363, 365) to accept and work with java.nio.file.Path directly instead of doing
toString() and new File(...). Replace any File-constructing logic with
Path-based APIs (Files.exists, Files.delete, Paths.get.resolve, Path.resolve,
etc.) or use File.toPath()/Path.toFile() only when an API strictly requires
java.io.File, and update method signatures/variables in BookService accordingly
so no unnecessary String round-trips remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f05decf7-cd58-4a74-9d4d-8377615a7791

📥 Commits

Reviewing files that changed from the base of the PR and between f6acfd3 and b1ff4e1.

📒 Files selected for processing (21)
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/main/java/org/booklore/service/email/SendEmailV2Service.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AudiobookProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Azw3Processor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/EpubProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Fb2Processor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/MobiProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/PdfProcessor.java
  • booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java
  • booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java
  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/CbxReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
✅ Files skipped from review due to trivial changes (7)
  • booklore-api/src/main/java/org/booklore/service/reader/PdfReaderService.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AudiobookProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Fb2Processor.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/test/java/org/booklore/service/reader/CbxReaderServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/MobiProcessor.java
  • booklore-api/src/main/java/org/booklore/service/metadata/BookMetadataService.java
🚧 Files skipped from review as they are similar to previous changes (7)
  • booklore-api/src/main/java/org/booklore/service/reader/CbxReaderService.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/PdfProcessor.java
  • booklore-api/src/test/java/org/booklore/service/email/SendEmailV2ServiceTest.java
  • booklore-api/src/test/java/org/booklore/service/reader/EpubReaderServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
  • booklore-api/src/test/java/org/booklore/service/book/BookServiceTest.java
  • booklore-api/src/main/java/org/booklore/service/email/SendEmailV2Service.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
booklore-api/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

booklore-api/src/**/*.java: Use 4-space indentation in Java files and match surrounding Java style
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection
Use MapStruct for entity/DTO mapping

Files:

  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/Azw3Processor.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/EpubProcessor.java
booklore-api/src/test/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

Prefer focused unit tests; use @SpringBootTest only when the Spring context is required

Files:

  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
🧠 Learnings (6)
📓 Common learnings
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 146
File: booklore-api/src/main/java/org/booklore/util/FileService.java:151-168
Timestamp: 2026-03-23T18:36:05.843Z
Learning: In `booklore-api/src/main/java/org/booklore/util/FileService.java`, the `"bin"` search path in `getSystemSearchPath()` is intentionally left as a relative path (not derived from `appProperties.getPathConfig()`), so binary lookup resolves relative to the process working directory. Do not flag this as an issue.
📚 Learning: 2026-03-23T18:36:05.843Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 146
File: booklore-api/src/main/java/org/booklore/util/FileService.java:151-168
Timestamp: 2026-03-23T18:36:05.843Z
Learning: In `booklore-api/src/main/java/org/booklore/util/FileService.java`, the `"bin"` search path in `getSystemSearchPath()` is intentionally left as a relative path (not derived from `appProperties.getPathConfig()`), so binary lookup resolves relative to the process working directory. Do not flag this as an issue.

Applied to files:

  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java
  • booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
📚 Learning: 2026-03-26T03:22:21.096Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 209
File: booklore-api/src/main/java/org/booklore/util/FileUtils.java:32-38
Timestamp: 2026-03-26T03:22:21.096Z
Learning: In `grimmory-tools/grimmory`, `BookFileEntity.fileSubPath` (in `booklore-api/src/main/java/org/booklore/model/entity/BookFileEntity.java`) is declared with `Column(name = "file_sub_path", length = 512, nullable = false)`, meaning it is never null. Do not flag null-guard concerns for this field.

Applied to files:

  • booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
📚 Learning: 2026-03-26T01:46:48.847Z
Learnt from: CR
Repo: grimmory-tools/grimmory PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T01:46:48.847Z
Learning: Applies to booklore-api/src/test/**/*.java : Prefer focused unit tests; use `SpringBootTest` only when the Spring context is required

Applied to files:

  • booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java
📚 Learning: 2026-03-24T18:46:47.249Z
Learnt from: pedronave
Repo: grimmory-tools/grimmory PR: 173
File: booklore-api/src/main/java/org/booklore/service/hardcover/HardcoverSyncService.java:188-199
Timestamp: 2026-03-24T18:46:47.249Z
Learning: In grimmory-tools/grimmory (HardcoverSyncService.java), when a `hardcoverBookId` is stored in book metadata but is malformed (non-numeric), the preferred behavior is to return null and skip the sync rather than falling back to ISBN-based lookup. The reasoning is that an explicitly set bookId represents deliberate user intent, and silently resolving via ISBN could sync progress to a different book than intended.

Applied to files:

  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
📚 Learning: 2026-03-25T19:09:09.638Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 189
File: booklore-api/src/main/java/org/booklore/service/kobo/KoboLibrarySyncService.java:113-116
Timestamp: 2026-03-25T19:09:09.638Z
Learning: In `booklore-api/src/main/java/org/booklore/service/kobo/KoboLibrarySyncService.java`, when deciding whether to forward the `Authorization` header to the upstream Kobo server proxy on the `/v1/library/sync` endpoint, a simple null check (`request.getHeader(HttpHeaders.AUTHORIZATION) != null`) is the correct guard. The header is either absent or present — if present it may or may not be a valid token, but validity is determined by the upstream Kobo server, not by Grimmory. Blank/whitespace intermediate states do not occur in practice, and pre-validating the token value before forwarding is not the responsibility of this service.

Applied to files:

  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
🔇 Additional comments (7)
booklore-api/src/main/java/org/booklore/util/FileUtils.java (1)

53-55: Good cleanup of the file-size path flow.

Passing the Path through directly removes the extra parse step cleanly.

booklore-api/src/main/java/org/booklore/service/reader/EpubReaderService.java (1)

209-220: Nice removal of the String -> Path round-trip.

Returning the Path from FileUtils directly keeps getBookPath(...) aligned with the new API.

booklore-api/src/main/java/org/booklore/service/fileprocessor/Azw3Processor.java (1)

73-76: Good direct Path to File handoff.

This keeps the extractor boundary on File without reintroducing an intermediate string.

booklore-api/src/main/java/org/booklore/service/fileprocessor/EpubProcessor.java (1)

74-77: Good direct Path to File handoff.

This keeps the cover extraction path aligned with the new utility contract.

booklore-api/src/test/java/org/booklore/util/FileUtilsTest.java (1)

37-49: Good test update for the new return type.

Asserting on Path directly avoids platform-specific separator handling in the test.

booklore-api/src/main/java/org/booklore/service/fileprocessor/CbxProcessor.java (1)

67-69: Nice propagation of the new Path contract through CBX processing.

All three call sites now convert straight to File at the integration boundary instead of round-tripping through String.

Also applies to: 91-93, 253-255

booklore-api/src/test/java/org/booklore/service/Rar5FallbackIntegrationTest.java (1)

75-77: Good static-stub alignment with production.

Returning a Path here keeps the fallback tests exercising the same contract as the reader code.

Also applies to: 97-99

Copy link
Copy Markdown
Member

@balazs-szucs balazs-szucs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense thanks.

Pathing is generally bit a of pain Java, I feel like, easy to get wrong still using string is a... questionable code. This is miles better! Thank you

@balazs-szucs balazs-szucs merged commit cacd1df into grimmory-tools:develop Mar 27, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants