Skip to content

refactor(author): sort authors using sort_name field - #1955

Open
zachyale wants to merge 5 commits into
developfrom
refactor/sort-authors-using-sort-name
Open

refactor(author): sort authors using sort_name field#1955
zachyale wants to merge 5 commits into
developfrom
refactor/sort-authors-using-sort-name

Conversation

@zachyale

@zachyale zachyale commented Jul 8, 2026

Copy link
Copy Markdown
Member

Description

This PR is the second part of the #1892 epic. It begins using the sort_name within the frontend-heavy sorting and filtering logic.

Linked Issue

Completes #1894

Changes

  • Serializes the sort_name field on author responses
  • Uses the sort field instead of the name field on backend order sorts
  • Serializes authorSortNames on book metadata as another string array (same order as author array, just using the sort values instead of proper names)
    • In an ideal world, instead of an array of strings to represent authors, it would be an array of objects that have a name field, and then can also have their sort fields. We aren't going to break/significantly modify the API response right now, so we defer to just including another array of sorted authors. When we have fully implemented pagination on all relevant models and customizable/overrideable sort names on relevant models, we can reevaluate our more core response structures for a future API v3.
  • Kill the manual author surname vorname logic on the F/E, to just use the author sorted names array

Manual Testing Steps

  1. Modify a sort name on the B/E
  2. Verify that when sorting by author surname the modified sort is properly displayed

Screenshots (Optional)

N/A

Additional Context (Optional)

Still non-user facing

AI Disclosure

N/A

Checklist

  • This PR links and implements an accepted issue.
  • This PR is a single focused change.
  • There are new or updated tests validating this change.
  • I ran just ui check and just api check.
  • I have added screenshots if there were any UI changes.
  • I have disclosed any AI usage as per the organization AI Policy above.
  • I understand all of my submitted changes.

Summary by CodeRabbit

  • New Features
    • Added sortName (and sortNameLocked) support for author details, summaries, and update requests.
    • Books now carry authorSortNames so author sorting can use the preferred values.
  • Bug Fixes
    • Author lists now sort by the preferred sort name when available (falling back to the regular name).
    • Sorting behavior recognizes additional “sort name” options.
    • Book author sorting now prefers authorSortNames, with fallback to existing author data.
  • Tests
    • Updated and expanded unit tests for author and book sorting and new DTO fields.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3382c8cf-1aec-47e6-9751-6dccdc697d6a

📥 Commits

Reviewing files that changed from the base of the PR and between 2b2bf10 and 154b164.

📒 Files selected for processing (2)
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • grimmory-tools/grimmory-docs (manual)
📜 Recent review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Test Suite / Frontend Tests
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Frontend Lint Threshold Check
🧰 Additional context used
📓 Path-based instructions (9)
frontend/src/**/*.{ts,tsx,html,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation in TypeScript, HTML, and SCSS in frontend code

Files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer inject() over constructor injection in frontend code

Files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
frontend/src/**/*.{ts,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/src/**/*.{ts,tsx,html}: Follow frontend/eslint.config.js: component selectors use app-, directive selectors use app, and any is disallowed in frontend code
Put user-facing strings in Transloco files under frontend/src/i18n/

Files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
frontend/src/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Vitest for tests in frontend code

Files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
**/*

⚙️ CodeRabbit configuration file

**/*: This project is being developed using current and future-facing technologies:

  • Java 25 with --enable-preview (preview features are INTENTIONAL and encouraged)
  • Spring Boot 4 (latest major version, check APIs accordingly)
  • Jackson 3 (new package: tools.jackson.* instead of com.fasterxml.jackson.*)
  • Hibernate 7.3.x (Jakarta Persistence 3.2, new APIs; avoid deprecated Hibernate 5/6 patterns)
  • Angular 21 (signals-based reactivity, no NgModules unless legacy)

Grimmory Internal Tools

Metadata Standards and Compliance

  • For all metadata writing and parsing logic, double-check against Dublin Core and ANSI standards to ensure perfect official compliance.
  • We strictly follow the widespread and official XML-compliant methods for EPUB2, EPUB3, CBX, and PDF formats.

General Java and Spring rules

  • ALWAYS prefer modern, idiomatic Java 25 constructs over legacy patterns.
  • Preview features (--enable-preview) are enabled and intentional; do NOT flag them as risky unless there is a concrete runtime issue.
  • Prefer: records, sealed classes/interfaces, pattern matching (switch expressions, instanceof), structured concurrency (StructuredTaskScope), scoped values, string templates, unnamed patterns/variables.
  • Prefer virtual threads (Thread.ofVirtual(), Executors.newVirtualThreadPerTaskExecutor()) over platform threads for I/O-bound work.
  • Prefer the new Sequenced Collections API (SequencedCollection, SequencedMap) where applicable.
  • Prefer var for local variables when the type is obvious from context.
  • Use stream().toList() instead of stream().collect(Collectors.toList()) for imm...

Files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
**/*.spec.ts

⚙️ CodeRabbit configuration file

**/*.spec.ts: Angular 21 test review:

  • Flag tests with no expect() calls.
  • Flag hardcoded async timeouts; prefer fakeAsync/tick or signal-based testing.
  • Flag missing fixture.detectChanges() after state mutations.

Files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
backend/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

backend/src/**/*.java: Use 4-space indentation and match surrounding Java style in backend code
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection in backend code
Use MapStruct for entity/DTO mapping in backend code
Keep JPA entities on the *Entity suffix in backend code

Files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
backend/src/test/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
**/*Test.java

⚙️ CodeRabbit configuration file

**/*Test.java: Java test review:

  • Prefer @ExtendWith(SpringExtension.class) or @SpringBootTest for integration tests.
  • Flag tests with no assertions.
  • Flag Thread.sleep() in tests; use Awaitility or virtual-thread-friendly alternatives.
  • Flag hardcoded ports or file paths.
  • Flag missing edge case coverage: null, empty, boundary values.
  • Prefer AssertJ over JUnit's built-in assertions for readability.
  • Prefer @Sql or Testcontainers for database state; not hand-rolled setup/teardown.

Files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
🧠 Learnings (12)
📚 Learning: 2026-04-05T21:16:01.715Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 385
File: frontend/src/app/app.component.ts:55-56
Timestamp: 2026-04-05T21:16:01.715Z
Learning: When reviewing code in the Grimmory frontend (Angular), prefer modern Angular patterns. Specifically: (1) Prefer `DestroyRef` with `takeUntilDestroyed(destroyRef)` for teardown in Angular v16+ instead of manually tracking `Subscription` arrays and calling `unsubscribe()` in `ngOnDestroy()`. (2) Prefer `inject()` for dependency injection over constructor injection where appropriate. (3) Prefer Angular signals (e.g., `signal`, `computed`) over `BehaviorSubject`/`Observable` for state where signals/computed values fit the use case. Flag older patterns when they can be replaced with these modern equivalents without changing behavior.

Applied to files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
📚 Learning: 2026-04-07T09:28:09.587Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 393
File: frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts:255-263
Timestamp: 2026-04-07T09:28:09.587Z
Learning: In this Angular frontend (under frontend/src/app/), flag manual resource management/cleanup patterns when there is an Angular v16+ automatic alternative. Examples to prefer: (1) Instead of manually pairing document/window event listeners with stored cleanup functions (e.g., add/removeEventListener with mouseMoveCleanup/documentClickCleanup/keydownCleanup/touchCleanup fields), register teardown via DestroyRef.onDestroy(cleanupFn) (or equivalent Angular v16+ teardown mechanism). (2) Instead of storing Subscriptions in fields and explicitly unsubscribing in ngOnDestroy (e.g., annotationSaveSubscription/annotationCacheSubscription), use takeUntilDestroyed(destroyRef) (piped into the observable) or other Angular v16+ primitives. (3) If teardown is lifecycle-coupled and can be automated via DestroyRef/takeUntilDestroyed/signals (or other Angular v16+ mechanisms), prefer the automated approach over manual ngOnDestroy cleanup. Raise a review finding for the manual pattern and recommend the aut...

Applied to files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
📚 Learning: 2026-04-11T03:55:57.229Z
Learnt from: zachyale
Repo: grimmory-tools/grimmory PR: 439
File: frontend/src/app/features/series-browser/components/series-browser/series-browser.component.ts:178-196
Timestamp: 2026-04-11T03:55:57.229Z
Learning: In this Angular frontend (frontend/src/app/), prefer the team’s reactive i18n “signal/computed” pattern: (1) For individual reactive translated strings, use `translateSignal()` from `jsverse/transloco`. (2) For option/label arrays that must update on language switch, create a single `activeLang` signal with `toSignal(t.langChanges$, { initialValue: t.getActiveLang() })`, then derive the arrays as `computed()` signals that read `activeLang()`. This should avoid manual `langChanges$` subscriptions and any `ngOnDestroy` subscription cleanup; prefer this over subscribing in `ngOnInit` when implementing reactive localization.

Applied to files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
📚 Learning: 2026-05-18T14:54:39.422Z
Learnt from: alexhb1
Repo: grimmory-tools/grimmory PR: 1379
File: frontend/src/assets/styles/tailwind.css:3-4
Timestamp: 2026-05-18T14:54:39.422Z
Learning: In the grimmory-tools/grimmory repository, Biome is not used for linting/formatting (no `biome.json` and no Biome dependency in `package.json`). During code reviews, do not raise Biome-related issues or recommend adding/changing `biome.json`/Biome dependencies for formatting or linting in this project.

Applied to files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
📚 Learning: 2026-06-30T01:30:43.644Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1881
File: frontend/src/app/shared/components/icon-picker/icon-picker-component.ts:116-119
Timestamp: 2026-06-30T01:30:43.644Z
Learning: In the Grimmory Angular frontend (Angular 21), do not flag `[(ngModel)]` two-way bindings that are bound directly to a `WritableSignal`. Angular supports two-way binding to writable signals, so usages like `[(ngModel)]="svgSearchText"` should be treated as valid and should not be considered an incorrect overwrite of the signal with a plain value. Only flag if the binding target is not a `WritableSignal`.

Applied to files:

  • frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
📚 Learning: 2026-04-10T08:15:37.436Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 449
File: booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java:139-145
Timestamp: 2026-04-10T08:15:37.436Z
Learning: When using Spring `ContentDisposition.builder(...).filename(name, StandardCharsets.UTF_8).build()` (i.e., explicitly providing UTF-8), the resulting header value should include both the quoted `filename="=?UTF-8?..."` and the RFC 5987 `filename*=` parameters. In this case, any extra ASCII fallback computation (e.g., deriving an ASCII `fallbackFilename` via `NON_ASCII_PATTERN` and calling `.filename(fallbackFilename)`) is likely redundant—prefer calling only `.filename(fallbackName?, StandardCharsets.UTF_8)` as appropriate and let Spring handle the UTF-8 header parameters. Verify by comparing the emitted header for `filename` and `filename*` before deciding to keep an ASCII fallback.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-04-14T12:43:08.698Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 502
File: booklore-api/src/main/java/org/booklore/service/reader/ChapterCacheService.java:0-0
Timestamp: 2026-04-14T12:43:08.698Z
Learning: For this codebase (booklore-api), target Java 25 with `--enable-preview`, so `_` is intentionally used as an unnamed/ignored variable (e.g., lambda parameter or pattern variable) per Java’s preview feature JEP 456. Do not flag `_` in those contexts as an invalid/reserved identifier; only flag it if it’s used in a non-supported position (e.g., where an unnamed variable is not applicable for the Java preview rules).

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-07T21:21:55.233Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1194
File: backend/src/main/java/org/booklore/service/ReadingSessionService.java:0-0
Timestamp: 2026-05-07T21:21:55.233Z
Learning: When reviewing Java 23+ code, treat `java.time.Instant#until(Instant endExclusive)` as a valid API/method call (it returns a `Duration`, equivalent to `Duration.between(this, endExclusive)`). Do not flag `instant.until(otherInstant)` as a compile error or API misuse when the project targets Java 25+ (as in grimmory-tools/grimmory); the call should be considered correct and returns a `Duration`.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-08T06:19:20.621Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1201
File: backend/src/main/java/org/booklore/model/dto/AccessTokenDto.java:3-10
Timestamp: 2026-05-08T06:19:20.621Z
Learning: For Jackson 3 codebases, do not treat imports from `com.fasterxml.jackson.annotation.*` (e.g., `JsonInclude`, `JsonProperty`, `JsonView`) as incorrect. In Jackson 3, `jackson-annotations` intentionally remains under `com.fasterxml.jackson.annotation.*` for backward compatibility, while only the core processing packages (e.g., `jackson-core`, `jackson-databind`) move to the `tools.jackson.*` namespace.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-04-27T15:25:55.042Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 930
File: backend/src/test/java/org/booklore/service/metadata/parser/ComicvineBookParserTest.java:68-75
Timestamp: 2026-04-27T15:25:55.042Z
Learning: In this repository’s JUnit 5 test sources (e.g., under backend/src/test/java/), do not flag “bare” `assert` statements as a bug. The project test runner is configured to always execute tests with assertions enabled (e.g., `-ea`), so `assert` behavior is consistent. Continue to review for correctness, but don’t treat unguarded `assert` usage in test classes as a static-analysis issue.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-22T03:20:45.559Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1446
File: backend/src/test/java/org/booklore/service/metadata/MetadataManagementServiceTest.java:465-470
Timestamp: 2026-05-22T03:20:45.559Z
Learning: In backend unit tests (Java files under backend/src/test/java), do not flag hardcoded placeholder filesystem paths (e.g., setting LibraryPathEntity.path = "/example") as violations when the value is intentionally unused for filesystem access. Only suppress the "no hardcoded paths" concern if the test does not perform any filesystem/network IO using that path (no reads/writes/Files.* calls or code paths that access the filesystem with that value); if the placeholder is actually used to touch the filesystem, flag it.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-04T05:01:33.919Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1081
File: backend/src/test/java/org/booklore/service/metadata/parser/AudibleParserTest.java:132-135
Timestamp: 2026-05-04T05:01:33.919Z
Learning: In grimmory-tools/grimmory unit/integration test classes (e.g., files matching **/*Test.java under backend/src/test/java/**), it is acceptable to directly instantiate Jackson mappers (e.g., `new ObjectMapper()` / `new JsonMapper(...)`) and this should NOT be flagged. The Jackson guidance to use Spring bean injection or `JsonMapper.shared()` applies only to production code; tests may construct dependencies directly as standard practice. Production-code mapper instantiation rules should still be enforced outside test sources.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
🔇 Additional comments (2)
frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts (1)

27-27: LGTM!

backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java (1)

70-94: LGTM!


Walkthrough

This PR adds sortName support for authors across backend DTOs, author ordering, and book metadata mapping, then mirrors those fields in frontend author and book models. Frontend author sorting now uses authorSortNames when present, with tests updated for the new payload shapes and fallback behavior.

Changes

Backend sortName propagation

Layer / File(s) Summary
Author DTO sortName fields
backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java, backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java, backend/src/main/java/org/booklore/model/dto/AuthorSummary.java, backend/src/main/java/org/booklore/model/dto/AuthorDetails.java, backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
Adds sortName to author DTOs, plus sortNameLocked to AuthorDetails and AuthorUpdateRequest.
AppAuthorService sortName population and ordering
backend/src/main/java/org/booklore/app/service/AppAuthorService.java, backend/src/main/java/org/booklore/repository/AuthorRepository.java
Populates sortName in author summary/detail builders and changes author ordering to use COALESCE(a.sortName, a.name) with new sort aliases.
BookMetadata author sort names mapping
backend/src/main/java/org/booklore/model/dto/BookMetadata.java, backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java, backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
Adds authorSortNames to BookMetadata and fills it from mapped authors, with mapper tests covering sort-name fallback behavior.

Frontend sortName model and sorting

Layer / File(s) Summary
Frontend author/book model updates
frontend/src/app/features/author-browser/model/author.model.ts, frontend/src/app/features/book/model/book.model.ts, frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts, frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts, frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts, frontend/src/app/features/author-browser/model/author.model.spec.ts, frontend/src/app/features/author-browser/components/author-editor/author-editor.component.spec.ts
Adds sortName and sortNameLocked to author models, adds authorSortNames to book metadata, and updates related fixtures.
SortService author sort extraction
frontend/src/app/features/book/service/sort.service.ts, frontend/src/app/features/book/service/sort.service.spec.ts
Uses authorSortNames for authorSurnameVorname sorting when available, and falls back to authors with updated tests.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related issues

Possibly related PRs

  • grimmory-tools/grimmory#1897: Introduces persisted author.sortName/sortNameLocked support that this PR consumes in DTOs, ordering, and sorting.

Suggested labels: backend, frontend, feature

Suggested reviewers: imnotjames

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commit format and clearly describes the author-sorting refactor.
Description check ✅ Passed The description includes all required sections, a clear change summary, linked issue, testing steps, AI disclosure, and checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/sort-authors-using-sort-name
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch refactor/sort-authors-using-sort-name

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java`:
- Around line 8-12: The AuthorUpdateRequest DTO already exposes sortName and
sortNameLocked, but AuthorMetadataService.updateAuthor is not applying them, so
add handling there to copy those request values onto the author metadata during
updates. Make sure the update flow treats sortName and sortNameLocked the same
way as the other editable fields in updateAuthor, using the existing author
update logic and identifiers in AuthorMetadataService and AuthorUpdateRequest to
locate the change.

In `@backend/src/main/java/org/booklore/repository/AuthorRepository.java`:
- Around line 25-28: Both AuthorRepository queries now sort by
COALESCE(a.sortName, a.name), so a regular index on sortName will not help this
ordering. Add a function-based/expression index for the same sort expression in
the database schema or migration used by AuthorEntity so the ordering in
findAllWithBookCount and findAllWithBookCountByLibraryIds can use an index. Keep
the index expression aligned exactly with the query’s COALESCE(a.sortName,
a.name) ordering, adjusting syntax to the target DB engine.

In `@backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java`:
- Around line 69-81: Add a new test in BookMetadataMapperTest to cover the
`@AfterMapping` fallback when an AuthorEntity has a null sortName. Build a
BookMetadataEntity with authors where one author’s sortName is null, call
bookMetadataMapper.toBookMetadata, and assert that dto.getAuthorSortNames() uses
the author’s name for that entry while preserving the author order; keep the
existing mapsAuthorSortNamesInAuthorOrder test as the happy path and add this
edge-case coverage alongside it.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: ccd1354e-8b3c-4b4e-8f73-651ddb1df9d4

📥 Commits

Reviewing files that changed from the base of the PR and between b5fc886 and 2b2bf10.

📒 Files selected for processing (18)
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • grimmory-tools/grimmory-docs (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Analyze (java-kotlin)
⚠️ CI failures not shown inline (2)

GitHub Actions: CI - Frontend Quality Thresholds / 0_Frontend Lint Threshold Check.txt: refactor(author): sort authors using sort_name field

Conclusion: failure

View job details

Current runner version: '2.335.1'
 ##[group]Runner Image Provisioner
 Hosted Compute Agent
 Version: 20260624.560
 Commit: 925d229a51159bc391ae97e54a2dd1fe20af789d
 Build Date:
 Worker ID: {af3bef76-1922-497c-bf47-c521b87ea100}
 Azure Region: northcentralus
 ##[endgroup]
 ##[group]Operating System
 Ubuntu
 24.04.4
 LTS
 ##[endgroup]
 ##[group]Runner Image
 Image: ubuntu-24.04
 Version: 20260628.225.1
 Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20260628.225/images/ubuntu/Ubuntu2404-Readme.md
 Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20260628.225
 ##[endgroup]
 ##[group]GITHUB_TOKEN Permissions
 Contents: read
 Metadata: read
 ##[endgroup]
 Secret source: Actions
 Prepare workflow directory
 Prepare all required actions
 Getting action download info
 Download action repository 'actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0' (SHA:9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)
 Download action repository 'pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271' (SHA:0ebf47130e4866e96fce0953f49152a61190b271)
 Download action repository 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' (SHA:48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e)
 Complete job name: Frontend Lint Threshold Check
 ##[group]Run actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
 with:
   repository: grimmory-tools/grimmory
   ***REDACTED***
   ssh-strict: true
   ssh-user: git
   persist-credentials: true
   clean: true
   sparse-checkout-cone-mode: true
   fetch-depth: 1
   fetch-tags: false
   show-progress: true
   lfs: false
   submodules: false
   set-safe-directory: true
   allow-unsafe-pr-checkout: false
 env:
   LINT_WARNING_THRESHOLD: 0
   LINT_ERROR_THRESHOLD: 0
   BUILD_WARNING_THRESHOLD: 0
 ##[endgroup]
 Syncing repository: grimmory-tools/grimmory
 ##[group]Getting Git version info
 Working directory is '/home/runner/work/grimmory/grimmory'
 [command]/usr/bin/git version
 git version 2.54.0
 #...

GitHub Actions: CI - Frontend Quality Thresholds / Frontend Lint Threshold Check: refactor(author): sort authors using sort_name field

Conclusion: failure

View job details

##[group]Run set +e
 �[36;1mset +e�[0m
 �[36;1mCI=1 NG_CLI_ANALYTICS=false pnpm run build:prod 2>&1 | tee build-output.log�[0m
 �[36;1mBUILD_EXIT_CODE=${PIPESTATUS[0]}�[0m
 �[36;1mset -e�[0m
 �[36;1m�[0m
 �[36;1mif [ "${BUILD_EXIT_CODE}" -ne 0 ]; then�[0m
 �[36;1m  echo "ERROR: Angular build failed with exit code ${BUILD_EXIT_CODE}"�[0m
 �[36;1m  exit ${BUILD_EXIT_CODE}�[0m
 �[36;1mfi�[0m
 shell: /usr/bin/bash -e {0}
 env:
   LINT_WARNING_THRESHOLD: 0
   LINT_ERROR_THRESHOLD: 0
   BUILD_WARNING_THRESHOLD: 0
   PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
 ##[endgroup]
 $ ng build --configuration production
 �[33m❯�[39m Building...
 �[32m✔�[39m Building...
 �[1mInitial chunk files�[22m�[2m | �[22m�[1mNames�[22m                 �[2m | �[22m �[1mRaw size�[22m�[2m | �[22m�[1mEstimated transfer size�[22m
 �[32mchunk-B26R76ML.js�[39m  �[2m | �[22m�[2m-�[22m                     �[2m | �[22m�[36m402.42 kB�[39m�[2m | �[22m              �[36m112.43 kB�[39m
 �[32mchunk-QDGHJ627.js�[39m  �[2m | �[22m�[2m-�[22m                     �[2m | �[22m�[36m363.23 kB�[39m�[2m | �[22m               �[36m73.55 kB�[39m
 �[32mmain-HI2LBWXC.js�[39m   �[2m | �[22m�[2mmain�[22m                  �[2m | �[22m�[36m209.33 kB�[39m�[2m | �[22m               �[36m39.65 kB�[39m
 �[32mstyles-IMX2ZJHM.css�[39m�[2m | �[22m�[2mstyles�[22m                �[2m | �[22m�[36m176.57 kB�[39m�[2m | �[22m               �[36m22.72 kB�[39m
 �[32mchunk-HMQJOBVF.js�[39m  �[2m | �[22m�[2m-�[22m                     �[2m | �[22m �[36m96.57 kB�[39m�[2m | �[22m               �[36m24.32 kB�[39m
 �[32mchunk-5KLJIJHQ.js�[39m  �[2m | �[22m�[2m-�[22m                     �[2m | �[22m �[36m50.31 kB�[39m�[2m | �[22m                �[36m9.80 kB�[39m
 �[32mchunk-3WGVLDJM.js�[39m  �[2m | �[22m�[2m-�[22m                     �[2m | �[22m �[36m49.57 kB�[39m�[2m | �[22m               �[36m11.49 kB�[39m
 �[32mchunk-EEDR3NAB.js�[39m  �[2m | �[22m�[2m-�[22m                     �[2m | �[22m �[36m45.43 kB�[39m�[2m | �[22m            ...
🧰 Additional context used
📓 Path-based instructions (13)
backend/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

backend/src/**/*.java: Use 4-space indentation and match surrounding Java style in backend code
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection in backend code
Use MapStruct for entity/DTO mapping in backend code
Keep JPA entities on the *Entity suffix in backend code

Files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
**/*

⚙️ CodeRabbit configuration file

**/*: This project is being developed using current and future-facing technologies:

  • Java 25 with --enable-preview (preview features are INTENTIONAL and encouraged)
  • Spring Boot 4 (latest major version, check APIs accordingly)
  • Jackson 3 (new package: tools.jackson.* instead of com.fasterxml.jackson.*)
  • Hibernate 7.3.x (Jakarta Persistence 3.2, new APIs; avoid deprecated Hibernate 5/6 patterns)
  • Angular 21 (signals-based reactivity, no NgModules unless legacy)

Grimmory Internal Tools

Metadata Standards and Compliance

  • For all metadata writing and parsing logic, double-check against Dublin Core and ANSI standards to ensure perfect official compliance.
  • We strictly follow the widespread and official XML-compliant methods for EPUB2, EPUB3, CBX, and PDF formats.

General Java and Spring rules

  • ALWAYS prefer modern, idiomatic Java 25 constructs over legacy patterns.
  • Preview features (--enable-preview) are enabled and intentional; do NOT flag them as risky unless there is a concrete runtime issue.
  • Prefer: records, sealed classes/interfaces, pattern matching (switch expressions, instanceof), structured concurrency (StructuredTaskScope), scoped values, string templates, unnamed patterns/variables.
  • Prefer virtual threads (Thread.ofVirtual(), Executors.newVirtualThreadPerTaskExecutor()) over platform threads for I/O-bound work.
  • Prefer the new Sequenced Collections API (SequencedCollection, SequencedMap) where applicable.
  • Prefer var for local variables when the type is obvious from context.
  • Use stream().toList() instead of stream().collect(Collectors.toList()) for imm...

Files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • frontend/src/app/features/book/service/sort.service.ts
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
  • frontend/src/app/features/author-browser/model/author.model.ts
**/dto/**/*.java

⚙️ CodeRabbit configuration file

**/dto/**/*.java: DTO review; prefer records:

  • Prefer Java records over classes for DTOs.
  • Jackson 3: use @JsonProperty, @JsonAlias, and @JsonIgnoreProperties(ignoreUnknown = true).
  • Flag missing validation annotations (@NotNull, @NotBlank, @Size) on input DTOs.
  • Flag ObjectMapper instantiation inside a DTO; must never happen.
  • New Jackson 3 package is tools.jackson.; flag any com.fasterxml.jackson. in new files.

Files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
frontend/src/**/*.{ts,tsx,html,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation in TypeScript, HTML, and SCSS in frontend code

Files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer inject() over constructor injection in frontend code

Files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
frontend/src/**/*.{ts,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/src/**/*.{ts,tsx,html}: Follow frontend/eslint.config.js: component selectors use app-, directive selectors use app, and any is disallowed in frontend code
Put user-facing strings in Transloco files under frontend/src/i18n/

Files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
frontend/src/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Vitest for tests in frontend code

Files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
**/*.spec.ts

⚙️ CodeRabbit configuration file

**/*.spec.ts: Angular 21 test review:

  • Flag tests with no expect() calls.
  • Flag hardcoded async timeouts; prefer fakeAsync/tick or signal-based testing.
  • Flag missing fixture.detectChanges() after state mutations.

Files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
**/repository/**/*.java

⚙️ CodeRabbit configuration file

**/repository/**/*.java: Spring Data / Hibernate repository review:

  • Prefer Spring Data JPA repository interfaces with typed methods.
  • Flag N+1 risks: use @EntityGraph or JOIN FETCH in JPQL for collection associations.
  • Flag missing @Transactional(readOnly = true) on query-only methods.
  • Flag any native query with string concatenation; use named parameters only.
  • Prefer @Query with JPQL over native SQL unless performance-critical.

Files:

  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
**/*.service.ts

⚙️ CodeRabbit configuration file

**/*.service.ts: Angular 21 service review:

  • Prefer providedIn: 'root' unless scope is intentionally limited.
  • Prefer inject() over constructor DI.
  • Prefer Signals or lightweight RxJS (takeUntilDestroyed) over manual subscription management.
  • Flag unsubscribed Observables (missing takeUntilDestroyed or explicit unsubscribe).
  • Prefer typed HttpClient responses with explicit error handling (catchError).
  • Flag any state mutation outside a defined signal or BehaviorSubject.

Files:

  • frontend/src/app/features/book/service/sort.service.ts
backend/src/test/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
**/*Test.java

⚙️ CodeRabbit configuration file

**/*Test.java: Java test review:

  • Prefer @ExtendWith(SpringExtension.class) or @SpringBootTest for integration tests.
  • Flag tests with no assertions.
  • Flag Thread.sleep() in tests; use Awaitility or virtual-thread-friendly alternatives.
  • Flag hardcoded ports or file paths.
  • Flag missing edge case coverage: null, empty, boundary values.
  • Prefer AssertJ over JUnit's built-in assertions for readability.
  • Prefer @Sql or Testcontainers for database state; not hand-rolled setup/teardown.

Files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
**/service/**/*.java

⚙️ CodeRabbit configuration file

**/service/**/*.java: Spring Framework 7 service layer review:

  • Flag missing @Transactional on methods that perform multiple writes.
  • Prefer constructor injection over @Autowired field injection. Use @AllArgsConstructor.
  • Use ApiError enum for throwing exceptions.
  • Flag checked exceptions swallowed silently; must log or rethrow.
  • Flag Thread.sleep(); prefer Duration-based overloads or ScheduledExecutorService.
  • Prefer virtual threads (Thread.ofVirtual()) for I/O-bound operations.
  • Flag mutable shared state in singleton beans.

Files:

  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
🧠 Learnings (19)
📚 Learning: 2026-04-10T08:15:37.436Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 449
File: booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java:139-145
Timestamp: 2026-04-10T08:15:37.436Z
Learning: When using Spring `ContentDisposition.builder(...).filename(name, StandardCharsets.UTF_8).build()` (i.e., explicitly providing UTF-8), the resulting header value should include both the quoted `filename="=?UTF-8?..."` and the RFC 5987 `filename*=` parameters. In this case, any extra ASCII fallback computation (e.g., deriving an ASCII `fallbackFilename` via `NON_ASCII_PATTERN` and calling `.filename(fallbackFilename)`) is likely redundant—prefer calling only `.filename(fallbackName?, StandardCharsets.UTF_8)` as appropriate and let Spring handle the UTF-8 header parameters. Verify by comparing the emitted header for `filename` and `filename*` before deciding to keep an ASCII fallback.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-04-14T12:43:08.698Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 502
File: booklore-api/src/main/java/org/booklore/service/reader/ChapterCacheService.java:0-0
Timestamp: 2026-04-14T12:43:08.698Z
Learning: For this codebase (booklore-api), target Java 25 with `--enable-preview`, so `_` is intentionally used as an unnamed/ignored variable (e.g., lambda parameter or pattern variable) per Java’s preview feature JEP 456. Do not flag `_` in those contexts as an invalid/reserved identifier; only flag it if it’s used in a non-supported position (e.g., where an unnamed variable is not applicable for the Java preview rules).

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-05-07T21:21:55.233Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1194
File: backend/src/main/java/org/booklore/service/ReadingSessionService.java:0-0
Timestamp: 2026-05-07T21:21:55.233Z
Learning: When reviewing Java 23+ code, treat `java.time.Instant#until(Instant endExclusive)` as a valid API/method call (it returns a `Duration`, equivalent to `Duration.between(this, endExclusive)`). Do not flag `instant.until(otherInstant)` as a compile error or API misuse when the project targets Java 25+ (as in grimmory-tools/grimmory); the call should be considered correct and returns a `Duration`.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-05-08T06:19:20.621Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1201
File: backend/src/main/java/org/booklore/model/dto/AccessTokenDto.java:3-10
Timestamp: 2026-05-08T06:19:20.621Z
Learning: For Jackson 3 codebases, do not treat imports from `com.fasterxml.jackson.annotation.*` (e.g., `JsonInclude`, `JsonProperty`, `JsonView`) as incorrect. In Jackson 3, `jackson-annotations` intentionally remains under `com.fasterxml.jackson.annotation.*` for backward compatibility, while only the core processing packages (e.g., `jackson-core`, `jackson-databind`) move to the `tools.jackson.*` namespace.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-05-04T20:31:11.075Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1086
File: backend/src/main/java/org/booklore/service/metadata/BookReviewUpdateService.java:63-66
Timestamp: 2026-05-04T20:31:11.075Z
Learning: For this repository, reviewers should treat string truncation done via `String.length()` and `String.substring(0, maxLength)` (UTF-16 code units) as an accepted, consistent convention. Do not flag individual occurrences of this pattern as bugs, even though it is not code-point-aware for surrogate pairs. A separate global effort is already tracked to move toward code-point-aware truncation, so per-site fixes should be avoided during code review.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-05-13T12:34:49.607Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 1293
File: backend/src/main/java/org/booklore/service/metadata/DuckDuckGoCoverService.java:246-252
Timestamp: 2026-05-13T12:34:49.607Z
Learning: In this repo’s Java code, when catching Jsoup `org.jsoup.HttpStatusException` (and similar exceptions originating from external libraries) and wrapping/rethrowing them, do not require preserving the original exception stack trace (e.g., as flagged by PMD `PreserveStackTrace`) as long as the application already captures the actionable diagnostics in logs or the thrown exception message (such as HTTP status code and the requested URL). Reviewers should still ensure the log/message contains those details; the intent is to avoid noisy stack traces that only reflect external-library internals rather than application code.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-05-17T13:38:16.462Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 1366
File: backend/src/main/java/org/booklore/service/FileStreamingService.java:65-66
Timestamp: 2026-05-17T13:38:16.462Z
Learning: In the grimmory-tools/grimmory repo, it’s an accepted pattern to pass the raw AccessDeniedException.getMessage() (even if it may include filesystem path details) into ApiError.PERMISSION_DENIED.createException(...). During code review, do not raise a security/information-disclosure issue solely based on that exception message being propagated to the API when using ApiError.PERMISSION_DENIED.createException with the AccessDeniedException message.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-05-23T23:01:25.769Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1456
File: backend/src/main/java/org/booklore/service/metadata/parser/GoodReadsParser.java:618-630
Timestamp: 2026-05-23T23:01:25.769Z
Learning: In this codebase (grimmory-tools/grimmory), it’s intentional to omit per-request timeouts on individual Java HttpRequest.Builder instances (e.g., GoodReadsParser.fetchJson). During reviews, do not flag missing builder-level timeouts as a best-practice violation; rely on framework-level and/or HttpClient-level timeouts configured elsewhere for consistent behavior. Only raise an issue if you can verify that no effective timeout is configured at the HttpClient/framework level.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-06-12T01:10:31.416Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1724
File: backend/src/main/java/org/booklore/repository/BookRepository.java:58-59
Timestamp: 2026-06-12T01:10:31.416Z
Learning: In this codebase (grimmory-tools/grimmory), reviews should not treat inline `LIMIT`/`OFFSET` clauses inside `Query` JPQL/HQL strings as a JPA compliance risk. This is intentional: `hibernate.jpa.compliance.query=true` is intentionally not set, and Hibernate 7.3+ supports `LIMIT`/`OFFSET` as valid HQL extensions. Therefore, do not flag or require changes to `Query` annotations solely due to `LIMIT`/`OFFSET` usage.

Applied to files:

  • backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java
  • backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java
  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/repository/AuthorRepository.java
  • backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-04-05T21:16:01.715Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 385
File: frontend/src/app/app.component.ts:55-56
Timestamp: 2026-04-05T21:16:01.715Z
Learning: When reviewing code in the Grimmory frontend (Angular), prefer modern Angular patterns. Specifically: (1) Prefer `DestroyRef` with `takeUntilDestroyed(destroyRef)` for teardown in Angular v16+ instead of manually tracking `Subscription` arrays and calling `unsubscribe()` in `ngOnDestroy()`. (2) Prefer `inject()` for dependency injection over constructor injection where appropriate. (3) Prefer Angular signals (e.g., `signal`, `computed`) over `BehaviorSubject`/`Observable` for state where signals/computed values fit the use case. Flag older patterns when they can be replaced with these modern equivalents without changing behavior.

Applied to files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
📚 Learning: 2026-04-07T09:28:09.587Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 393
File: frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts:255-263
Timestamp: 2026-04-07T09:28:09.587Z
Learning: In this Angular frontend (under frontend/src/app/), flag manual resource management/cleanup patterns when there is an Angular v16+ automatic alternative. Examples to prefer: (1) Instead of manually pairing document/window event listeners with stored cleanup functions (e.g., add/removeEventListener with mouseMoveCleanup/documentClickCleanup/keydownCleanup/touchCleanup fields), register teardown via DestroyRef.onDestroy(cleanupFn) (or equivalent Angular v16+ teardown mechanism). (2) Instead of storing Subscriptions in fields and explicitly unsubscribing in ngOnDestroy (e.g., annotationSaveSubscription/annotationCacheSubscription), use takeUntilDestroyed(destroyRef) (piped into the observable) or other Angular v16+ primitives. (3) If teardown is lifecycle-coupled and can be automated via DestroyRef/takeUntilDestroyed/signals (or other Angular v16+ mechanisms), prefer the automated approach over manual ngOnDestroy cleanup. Raise a review finding for the manual pattern and recommend the aut...

Applied to files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
📚 Learning: 2026-04-11T03:55:57.229Z
Learnt from: zachyale
Repo: grimmory-tools/grimmory PR: 439
File: frontend/src/app/features/series-browser/components/series-browser/series-browser.component.ts:178-196
Timestamp: 2026-04-11T03:55:57.229Z
Learning: In this Angular frontend (frontend/src/app/), prefer the team’s reactive i18n “signal/computed” pattern: (1) For individual reactive translated strings, use `translateSignal()` from `jsverse/transloco`. (2) For option/label arrays that must update on language switch, create a single `activeLang` signal with `toSignal(t.langChanges$, { initialValue: t.getActiveLang() })`, then derive the arrays as `computed()` signals that read `activeLang()`. This should avoid manual `langChanges$` subscriptions and any `ngOnDestroy` subscription cleanup; prefer this over subscribing in `ngOnInit` when implementing reactive localization.

Applied to files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
📚 Learning: 2026-05-18T14:54:39.422Z
Learnt from: alexhb1
Repo: grimmory-tools/grimmory PR: 1379
File: frontend/src/assets/styles/tailwind.css:3-4
Timestamp: 2026-05-18T14:54:39.422Z
Learning: In the grimmory-tools/grimmory repository, Biome is not used for linting/formatting (no `biome.json` and no Biome dependency in `package.json`). During code reviews, do not raise Biome-related issues or recommend adding/changing `biome.json`/Biome dependencies for formatting or linting in this project.

Applied to files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
📚 Learning: 2026-06-30T01:30:43.644Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1881
File: frontend/src/app/shared/components/icon-picker/icon-picker-component.ts:116-119
Timestamp: 2026-06-30T01:30:43.644Z
Learning: In the Grimmory Angular frontend (Angular 21), do not flag `[(ngModel)]` two-way bindings that are bound directly to a `WritableSignal`. Angular supports two-way binding to writable signals, so usages like `[(ngModel)]="svgSearchText"` should be treated as valid and should not be considered an incorrect overwrite of the signal with a plain value. Only flag if the binding target is not a `WritableSignal`.

Applied to files:

  • frontend/src/app/features/author-browser/model/author.model.spec.ts
  • frontend/src/app/features/book/model/book.model.ts
  • frontend/src/app/features/book/service/sort.service.spec.ts
  • frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts
  • frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts
  • frontend/src/app/features/book/service/sort.service.ts
  • frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts
  • frontend/src/app/features/author-browser/model/author.model.ts
📚 Learning: 2026-05-02T18:47:09.753Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1052
File: backend/src/main/java/org/booklore/model/dto/kobo/KoboDeals.java:14-33
Timestamp: 2026-05-02T18:47:09.753Z
Learning: In this codebase, do not raise review findings suggesting conversion of class-based DTOs to Java `record`s (e.g., DTOs under `org.booklore.model.dto.*` such as `org.booklore.model.dto.kobo`). Although records may be a preferred guideline in general, the team is intentionally avoiding new `record` DTOs right now due to low existing adoption—so class-based DTOs should not be flagged solely for not being records.

Applied to files:

  • backend/src/main/java/org/booklore/model/dto/BookMetadata.java
  • backend/src/main/java/org/booklore/model/dto/AuthorDetails.java
  • backend/src/main/java/org/booklore/model/dto/AuthorSummary.java
  • backend/src/main/java/org/booklore/model/dto/request/AuthorUpdateRequest.java
📚 Learning: 2026-04-27T15:25:55.042Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 930
File: backend/src/test/java/org/booklore/service/metadata/parser/ComicvineBookParserTest.java:68-75
Timestamp: 2026-04-27T15:25:55.042Z
Learning: In this repository’s JUnit 5 test sources (e.g., under backend/src/test/java/), do not flag “bare” `assert` statements as a bug. The project test runner is configured to always execute tests with assertions enabled (e.g., `-ea`), so `assert` behavior is consistent. Continue to review for correctness, but don’t treat unguarded `assert` usage in test classes as a static-analysis issue.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-22T03:20:45.559Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1446
File: backend/src/test/java/org/booklore/service/metadata/MetadataManagementServiceTest.java:465-470
Timestamp: 2026-05-22T03:20:45.559Z
Learning: In backend unit tests (Java files under backend/src/test/java), do not flag hardcoded placeholder filesystem paths (e.g., setting LibraryPathEntity.path = "/example") as violations when the value is intentionally unused for filesystem access. Only suppress the "no hardcoded paths" concern if the test does not perform any filesystem/network IO using that path (no reads/writes/Files.* calls or code paths that access the filesystem with that value); if the placeholder is actually used to touch the filesystem, flag it.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-04T05:01:33.919Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1081
File: backend/src/test/java/org/booklore/service/metadata/parser/AudibleParserTest.java:132-135
Timestamp: 2026-05-04T05:01:33.919Z
Learning: In grimmory-tools/grimmory unit/integration test classes (e.g., files matching **/*Test.java under backend/src/test/java/**), it is acceptable to directly instantiate Jackson mappers (e.g., `new ObjectMapper()` / `new JsonMapper(...)`) and this should NOT be flagged. The Jackson guidance to use Spring bean injection or `JsonMapper.shared()` applies only to production code; tests may construct dependencies directly as standard practice. Production-code mapper instantiation rules should still be enforced outside test sources.

Applied to files:

  • backend/src/test/java/org/booklore/mapper/BookMetadataMapperTest.java
📚 Learning: 2026-05-07T21:37:46.988Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 1194
File: backend/src/main/java/org/booklore/service/ReadingSessionService.java:121-123
Timestamp: 2026-05-07T21:37:46.988Z
Learning: In grimmory-tools/grimmory service-layer code, if arithmetic overflow occurs inside inference/business-logic (e.g., when deriving inferred fields like durationSeconds from start/end timestamps), treat it as a server-side anomaly. Prefer letting the global exception handler translate it into a generic 5xx response rather than throwing an explicit ApiError 4xx (e.g., do not convert overflow into a client error).

Applied to files:

  • backend/src/main/java/org/booklore/app/service/AppAuthorService.java
🔇 Additional comments (16)
frontend/src/app/features/author-browser/model/author.model.ts (1)

4-4: LGTM!

Also applies to: 44-48, 77-81

frontend/src/app/features/book/model/book.model.ts (1)

202-202: LGTM!

frontend/src/app/features/author-browser/components/author-card/author-card.component.spec.ts (1)

238-241: LGTM!

frontend/src/app/features/author-browser/components/author-detail/author-detail.component.spec.ts (1)

37-41: LGTM!

frontend/src/app/features/author-browser/components/author-match/author-match.component.spec.ts (1)

143-146: LGTM!

frontend/src/app/features/author-browser/model/author.model.spec.ts (1)

62-62: LGTM!

frontend/src/app/features/book/service/sort.service.ts (1)

66-67: LGTM!

frontend/src/app/features/book/service/sort.service.spec.ts (1)

58-60: LGTM!

Also applies to: 72-82

backend/src/main/java/org/booklore/app/dto/AppAuthorDetail.java (1)

17-17: LGTM!

backend/src/main/java/org/booklore/app/dto/AppAuthorSummary.java (1)

17-17: LGTM!

backend/src/main/java/org/booklore/model/dto/AuthorSummary.java (1)

17-17: LGTM!

backend/src/main/java/org/booklore/model/dto/AuthorDetails.java (1)

13-17: LGTM!

backend/src/main/java/org/booklore/app/service/AppAuthorService.java (2)

89-89: LGTM!

Also applies to: 132-132


191-196: LGTM!

backend/src/main/java/org/booklore/model/dto/BookMetadata.java (1)

64-64: LGTM!

backend/src/main/java/org/booklore/mapper/BookMetadataMapper.java (1)

19-27: LGTM!

Comment on lines +25 to +28
@Query("SELECT a, COUNT(bm) FROM AuthorEntity a LEFT JOIN a.bookMetadataEntityList bm GROUP BY a ORDER BY COALESCE(a.sortName, a.name)")
List<Object[]> findAllWithBookCount();

@Query("SELECT a, COUNT(DISTINCT bm) FROM AuthorEntity a LEFT JOIN a.bookMetadataEntityList bm JOIN bm.book b WHERE b.library.id IN :libraryIds GROUP BY a ORDER BY a.name")
@Query("SELECT a, COUNT(DISTINCT bm) FROM AuthorEntity a LEFT JOIN a.bookMetadataEntityList bm JOIN bm.book b WHERE b.library.id IN :libraryIds GROUP BY a ORDER BY COALESCE(a.sortName, a.name)")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🔵 Trivial

LGTM!

Consider a function-based index for the COALESCE sort expression.

Both findAllWithBookCount and findAllWithBookCountByLibraryIds now order by COALESCE(a.sortName, a.name). A plain index on sortName won't be used by the database for this expression. If author tables grow large, evaluate a function-based/expression index (e.g., CREATE INDEX ... ON author (COALESCE(sort_name, name)) — syntax varies by DB engine) to keep these queries efficient.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/java/org/booklore/repository/AuthorRepository.java` around
lines 25 - 28, Both AuthorRepository queries now sort by COALESCE(a.sortName,
a.name), so a regular index on sortName will not help this ordering. Add a
function-based/expression index for the same sort expression in the database
schema or migration used by AuthorEntity so the ordering in findAllWithBookCount
and findAllWithBookCountByLibraryIds can use an index. Keep the index expression
aligned exactly with the query’s COALESCE(a.sortName, a.name) ordering,
adjusting syntax to the target DB engine.

@coderabbitai
coderabbitai Bot requested a review from imnotjames July 9, 2026 19:11
@coderabbitai coderabbitai Bot added feature and removed enhancement labels Jul 9, 2026
@imnotjames

Copy link
Copy Markdown
Contributor

This feels more like a feature at this point. It's far outside of refactor territory, and the previous sort name code was pushing the definition pretty far.

Comment thread frontend/src/app/features/book/service/sort.service.ts
@imnotjames

Copy link
Copy Markdown
Contributor

Per feedback in Discord I am waiting for this to be renamed to file_as

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.

2 participants