Spring Boot REST API for borrowing shared library resources (books, equipment, AV kits) with FIFO waitlists and time-limited reservations.
- Java 17+
- Maven 3.9+
mvn spring-boot:runAPI base URL: http://localhost:8080
H2 file database persists under ./data/librarydb.mv.db. H2 console: http://localhost:8080/h2-console (JDBC URL jdbc:h2:file:./data/librarydb, user sa, empty password).
mvn testIntegration tests use in-memory H2 with library.reservation.claim-window-hours=0 to exercise expiry cascade in one request.
| Capability | Method & path |
|---|---|
| Create resource | POST /api/resources |
| Create employee | POST /api/employees |
| Borrow (or join waitlist) | POST /api/resources/{resourceId}/borrow |
| Return copy | POST /api/resources/{resourceId}/return |
| View waitlist | GET /api/resources/{resourceId}/waitlist |
| Leave waitlist | DELETE /api/resources/{resourceId}/waitlist/{employeeId} |
| Claim reservation | POST /api/reservations/{reservationId}/claim |
| Process expired reservations | POST /api/reservations/process-expired |
| Employee activity | GET /api/employees/{employeeId}/activity |
Request bodies for borrow/return/claim:
{ "employeeId": 1 }Create resource:
{ "title": "Clean Code", "type": "BOOK", "totalCopies": 2 }type enum: BOOK, EQUIPMENT, AV_KIT.
- Resource — catalog item with
totalCopies - BorrowRecord — active/historical loans (
returned_atnull = active) - WaitlistEntry — FIFO queue per resource (
WAITING→RESERVED→FULFILLED/LEFT) - Reservation — separate entity with lifecycle (
PENDING→CLAIMED/EXPIRED), linked to waitlist entry
Availability:
available = totalCopies - activeBorrows - pendingReservations
No background scheduler. POST /api/reservations/process-expired checks pending reservations past expires_at, marks them EXPIRED, removes the holder from the waitlist, and offers the freed copy to the next WAITING entry with a fresh claim window. The service loops until no expired pending reservations remain, so chained expiries advance through the queue in one call.
On return, if the waitlist has WAITING entries, a reservation is created for the front person immediately.
Default claim window: 24 hours (library.reservation.claim-window-hours in application.properties).
- One borrow record per employee per resource at a time (no double-borrow).
- Employee cannot join the same waitlist twice while
WAITINGorRESERVED. - Employee with
RESERVEDstatus cannot leave waitlist until they claim or the reservation expires. - Borrow endpoint auto-joins waitlist when no copy is available (not rejected).
process-expiredis the documented mechanism for expiry; callers invoke it on a schedule or after the claim window.
| Code | When |
|---|---|
| 201 | Resource/employee created; borrow/waitlist join |
| 200 | Successful read or mutation |
| 204 | Left waitlist |
| 400 | Validation failure |
| 404 | Missing resource/employee/reservation/waitlist entry |
| 409 | Business rule conflict |
See schema.sql for the full DDL. The schema models Resource, WaitlistEntry, and Reservation as separate tables so reservation expiry can release a copy and advance the FIFO queue without overloading a single reserved_by column on the resource.
erDiagram
RESOURCES ||--o{ BORROW_RECORDS : has
EMPLOYEES ||--o{ BORROW_RECORDS : makes
RESOURCES ||--o{ WAITLIST_ENTRIES : has
EMPLOYEES ||--o{ WAITLIST_ENTRIES : joins
WAITLIST_ENTRIES ||--o| RESERVATIONS : triggers
RESOURCES ||--o{ RESERVATIONS : holds
EMPLOYEES ||--o{ RESERVATIONS : assigned_to
RESOURCES {
bigint id PK
string title
string type
int total_copies
}
EMPLOYEES {
bigint id PK
string name
string email UK
}
BORROW_RECORDS {
bigint id PK
bigint resource_id FK
bigint employee_id FK
timestamp borrowed_at
timestamp returned_at
}
WAITLIST_ENTRIES {
bigint id PK
bigint resource_id FK
bigint employee_id FK
timestamp joined_at
string status
}
RESERVATIONS {
bigint id PK
bigint waitlist_entry_id FK
bigint resource_id FK
bigint employee_id FK
timestamp created_at
timestamp expires_at
string status
}
Availability is derived at runtime:
available = total_copies − active_borrows − pending_reservations
| File | Purpose |
|---|---|
api-tests.http |
Happy path, all rule violations, validation, 404s |
api-tests-expiry-cascade.http |
Multi-step expiry cascade + claim-after-expiry (start app with --library.reservation.claim-window-hours=0) |
postman/Library-Resource-Booking-Oman.postman_collection.json |
Postman collection (Alharith + Omani resources, ordered steps) |
Automated evidence: mvn test (includes expiry-cascade integration test).
src/main/java/com/library/
controller/ REST layer
service/ Business rules
repository/ JPA persistence
model/ Entities & enums
dto/ Request/response records
exception/ 404/409/400 handling
- Spring Boot REST API with SQL persistence (H2 file mode)
- All required endpoints implemented
- Business rules enforced (409 for conflicts)
- Layered architecture (controller / service / repository)
-
schema.sql+ ER diagram above - README with run instructions, assumptions, and design notes
- API test collection (
.httpfiles) with rule violations and expiry cascade - Integration tests (
mvn test)
To submit:
git init
git add .
git commit -m "Submit Library Resource Booking API"
git remote add origin <your-repo-url>
git push -u origin mainOr zip the project (exclude target/ and data/).