Skip to content

Make directory libraries reachable through the REST API - #745

Open
koppor wants to merge 24 commits into
directory-convertfrom
directory-rest-api
Open

koppor wants to merge 24 commits into
directory-convertfrom
directory-rest-api

Conversation

@koppor

@koppor koppor commented Jul 22, 2026 •

Copy link
Copy Markdown
Member

📚 Directory-as-library stack — bottom → top, each builds on the one below:

  1. Programmatic Hayagriva writer (directory-as-library, phase 0) #736 · Hayagriva YAML writer
  2. Open folder as library (directory as library, phase 1) #737 · Open folder as library
  3. Live inbound sync for directory libraries (phase 2) #738 · Inbound file sync
  4. Write-back to Hayagriva sidecars (directory as library, phase 3) #739 · Write-back to sidecars
  5. Groups panel mirrors the directory structure (phase 4, #10930) #740 · Directory-structure groups
  6. Pattern-driven pair renames for directory libraries (phase 5a) #741 · Pattern-driven pair renames
  7. Mirror directory libraries into a .bib with git-sync merge-back #743 · .bib mirror + merge-back
  8. Convert .bib libraries into directory libraries #744 · Convert .bib → directory
  9. Make directory libraries reachable through the REST API #745 · REST API support ← this PR
  10. Split DirectoryLibrarySynchronizer into collaborators #760 · Split the synchronizer

Related issues and pull requests

Phase 8 of the "directory as library" plan (PLAN.md), stacked on #744 (base branch directory-convert). No issue is closed by this PR.

PR Description

🤖 JabRef's REST API identified every open library by its .bib path, so directory libraries — which have only a root folder — were invisible: absent from the library list and the existence-check query, and unreachable for reading or adding entries. They are now identified by their root, the same identity the GUI already uses, so listing, queries, reads, and appends all resolve a directory library; added entries flow through into its sidecars and mirror. This covers GUI mode.

jabref-contrib-policy:4.2:reviewed​:ok

Analogies

Like honey the label finally lists, the directory library was always in the pantry but unnamed on the jar; now the index reads it out. Like a chocolate assortment where one praline had no map coordinate, every piece can now be pointed to and picked. And like the moon answering to a name in the almanac, the library was always orbiting — it just needed an entry in the tables to be found and hailed.

Steps to test

  1. Start JabRef with the HTTP server enabled and open a folder as library (so a directory-library tab is open).
  2. GET http://localhost:23119/libraries — the directory library now appears in the list (id = <folder-name>-<hash>).
  3. POST http://localhost:23119/libraries:query with {"queries": ["doi = \"…\""]} — matches in the directory library are returned (the browser-extension / JabMap existence check).
  4. POST http://localhost:23119/libraries/<that-id>/entries with a BibTeX body — the entry lands in the directory library, and a Markdown sidecar plus the .bib mirror update on disk.

(No user-visible UI change, so no screenshot — the behaviour is exercised by ServerUtilsTest and the steps above.)

Live verification (running JabRef + HTTP server)

Enabled the HTTP server, opened a folder as library (demo-library), and drove the real API:

  • GET /libraries → the directory library now appears: "demo-library-275b20a6" (a .bib-only listing before this PR).
  • POST /libraries:query {"queries":["title = \"ZygOS: Achieving Low Tail Latency\""]} → {"libraryId":"demo-library-275b20a6","entryId":"zygos"} (existence check hits the directory library); a non-matching query returns [].
  • GET /libraries/demo-library-275b20a6/entries/sidecar2026 → entry preview resolves.
  • POST /libraries/demo-library-275b20a6/entries with a BibTeX body → 204; the entry lands in the directory library, and write-back creates the Markdown sidecar Rest2026 - Added Over the REST API.md (filename pattern applied) and updates the .bib mirror. A follow-up libraries:query then finds the freshly added Rest2026 — the full loop closes.

Directory-library table after adding an entry over the REST API

Known cosmetic follow-up (not fixed here): the interactive import dialog's "Library to import into" selector shows untitled for a directory library (it derives the name from the .bib path); the import still targets the correct directory-library context.

AI usage

Claude Code (model claude-opus-4-8).

AI CHECKLIST.md walkthrough
  • No == null / != null checks.
  • No Objects.requireNonNull(...).
  • [/] New classes annotated with @NullMarked — no new production classes (methods added to existing ServerUtils).
  • Optional consumed with map / or / flatMap / orElse(false) — no isPresent() + get() (in fact this PR removes a getDatabasePath().get() block).
  • [/] StringUtil.isBlank(...) — no blank checks added.
  • No catch (Exception e).
  • No throw new RuntimeException(...).
  • [/] Logged exceptions last — no new logging.
  • [/] Withers for new BibEntry — none built.
  • Modern Java (Optional.or, streams).
  • [/] Precompiled regex — none.
  • [/] No new Thread() — none.
  • No commented-out code, no trivial comments, no AI-disclosure comments.
  • Markdown Javadoc (///) with Markdown syntax.
  • [/] Localized user-facing text — none added (id strings, not shown to users).
  • Security: the existing HtmlEscapers on the 404 id message is preserved.
  • Behavior change in jabsrv covered by ServerUtilsTest (listing, path resolution, context resolution, unknown id).
  • Tests assert with assertEquals/assertSame, plain JUnit, no @DisplayName, no caught exceptions, @TempDir.
  • ./gradlew :jabsrv:test green locally (full suite, no regressions); :jabsrv:compileJava + :jabgui:compileJava green.
  • ./gradlew checkstyleMain checkstyleTest green for touched modules.
  • ./gradlew modernizer green.
  • ./gradlew :rewriteRun applied (no diff).
  • ./gradlew traceRequirements green (req~directory-library.rest-api~1 covered impl + utest).
  • npx markdownlint-cli2 green on the changed Markdown.
  • [/] Docker IntelliJ formatter — not available; CI format job is the backstop.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (see "Live verification" above: enabled the HTTP server, listed/queried/read/added against an open directory library, and confirmed the sidecar + mirror were written on disk)
  • I added JUnit tests for changes (if applicable)
  • [/] I added screenshots in the PR description (if change is visible to the user) — no UI change
  • [/] I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • [/] I added one sentence (max 20 words) to CHANGELOG.md describing the change from the user's point of view (if the change is visible to the user)
  • I checked the user documentation — the docs PR is the pending phase-5 step covering the whole feature

🤖 Generated with Claude Code

koppor and others added 10 commits July 22, 2026 18:20
jabsrv identified every library by its .bib path, so directory
libraries - which have no .bib path, only a root directory - were
invisible to the whole HTTP API: absent from the library listing and
the existence-check query, and un-addressable for reading or adding
entries.

ServerUtils now derives a library's id from getDatabasePath() or, for
a directory library, its root directory (the same identity the GUI
session store uses), so listing, the batch query, entry reads, and
appends all resolve it. The GUI append matcher gains the same
fallback, so an append targets the open directory-library tab by its
root; the entries then flow through the normal write-back into
sidecars and the mirror.

Standalone-server mode still serves only .bib files (adds require the
GUI regardless).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
Resync layer 9 (tip) of the stack (no conflicts; jabsrv ServerUtils
changes still compile and ServerUtilsTest passes against upstream).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
The id derives from the library's location on disk in one place; endpoints that need BibTeX bytes read a directory library's mirror file, and the select command matches directory libraries too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vr3E1Gg5DRU4LQDDVnhPys
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vr3E1Gg5DRU4LQDDVnhPys
koppor and others added 3 commits September 7, 2026 00:16
Fixes the CI format check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
Upstream added a 0071 ADR after this stack claimed the number, so the
MADR duplicate-ID check failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
koppor and others added 6 commits September 8, 2026 00:22
# Conflicts:
#	jabsrv/src/main/java/org/jabref/http/server/command/SelectEntriesCommand.java
#	jabsrv/src/main/java/org/jabref/http/server/services/ServerUtils.java
Main's new library id ignored directory libraries, so in-app entry links could not find them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CajtxjcCEjS87f4hoTBTJD

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant