- Local requirements
- How to set up local database
- How do I start up the application?
- Liquibase migrations
- Image API endpoint
- Authorize endpoint
- Book endpoints
- Reservation endpoint
Before setting up the local database, ensure you have the following installed on your system:
- Docker – Required to run the database container.
- Docker Compose (included with Docker Desktop).
- Java 21+ – Required if running the application locally from the IDE.
The code style settings should be automatically detected if you open the most recent version of the project. To verify that the appropriate code style is used you can do the following:
Go to File -> Settings ->
Navigate to Editor -> Code Style > Java. ->
Ensure GoogleStyle is selected in the Scheme dropdown.
SPRING_PROFILES_ACTIVE=local ./gradlew check
docker-compose up -d
docker ps
docker logs books-db
docker rm -f books-db
docker stop books-db
docker start books-db
docker ps
If container with name:"books-db" not found repeat steps in How to setup local database?
To run application with local configuration using IDE we need specify in it configuration properties.
- Navigate
Run > Edit Configurations... > Active profile:- Write profile:
local - If active profile input field don't show:
- Press
Alt+Mand in opened context menu find and selectActive profile
- Write profile:
- Apply and from now by default using short-cut:
Shift+F10application going to start usingapplication-local.properties
Run this command:
./gradlew bootRun --args='--spring.profiles.active=local'
Running application with ./gradlew bootRun or starting in IDE without adding active profile local will trigger deployed application.properties configuration and application start will fail.
We are using Liquibase to manage database schemas, version and rollbacks control, prevent unexpected changes.
For the functionality and example purpose test_table is created with
changelog-test-create-table-1.yaml and filled up with three rows of data using
changelog-test-fill-table-2.yaml.
By sending GET request it require parameter variable name.
Request URL must look like /api/image?name=1984.png.
Image name can be found in Book object, field named image.
- Response body (if image by name exist) should return BLOB "Binary Large Object" with HTTP status code 200.
- Response body (if image name parameter given empty or null):
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Image name cannot be null or empty",
"instance": "/api/image"
- Response body (if image by name not exist):
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "Image with name: 'NoImage' not found",
"instance": "/api/image"
Requesting /api/image endpoint, header must contain 'Content-Type': 'multipart/form-data'.
Body use FormData with key parameter image with value from <input> tag defined type as img.
Validations apply to image on upload:
- Acceptable image format (.png .jpeg .jpg),
- No bigger than 1MB image,
- Image file name length limited to 70 characters,
- Empty image can't be uploaded,
- Image file naming must follow unique value.
- Response body (if upload was successful):
"message": "Image successfully saved.",
"fileName": "911630551152721a740adf98a86a8795.png",
"fileType": "PNG",
"fileSize": "247.18 KB"
- Response body (if image type don't match required):
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Make sure to select a image and use JPEG, JPG or PNG format.",
"instance": "/api/image"
- Response body (if saving duplicate images):
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "Image with file name: 911630551152721a740adf98a86a8795.png already exist.",
"instance": "/api/image"
Add @PreAuthorize("isAuthenticated()") annotation to endpoint.
Note: "isAuthenicated" can later be changed to role-based authentication.
After get(BASE_URL) make sure to add .with(jwtAuthentication()).
This will get authentication from Security Context.
Regular GET request to /api/books returns an array list of all books with this body:
{
"id": "9d802225-8130-41cd-b5b9-21d587c10106",
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"isAvailable": true,
"image": "The_great_gatsby.png"
}
Making a request to /api/books/{bookId} with a valid bookId as a PathVariable returns the details of the book. The isAvailable field is determined by checking if there are any available book copies for the given bookId. Here is an example of returned body:
{
"id": "306af5b7-212f-4bb1-9be4-36c498990b48",
"isbn": "9783518371435",
"title": "Die Verwandlung",
"author": "Franz Kafka",
"image": "Die_verwandlung.png",
"description": "The story of a man who wakes up one morning to find himself transformed into a huge insect.",
"bookFormat": "PAPERBACK",
"pages": 96,
"publicationDate": "2015-10-15",
"publisher": "Suhrkamp Verlag",
"language": "GERMAN",
"series": "European Classics",
"isAvailable": true,
"copies": [
{
"name": "KAUNAS",
"numOfCopies": 1
},
{
"name": "VILNIUS",
"numOfCopies": 1
}
]
}
Making a request to /api/offices/books/{bookId} with a valid bookId as a PathVariable returns a list of offices where the book is available, along with the number of book copies available in this body:
[
{
"id": "8c3d9c95-c0ca-4cc5-8612-e57f01f9ab6f",
"name": "KAUNAS",
"numOfCopiesAvailable": 2,
"address": "A. Juozapavičiaus pr. 11d, LT-45257 Kaunas, Lithuania"
},
{
"id": "d0d76414-0e65-4d7c-a6e1-a8491b21db2d",
"name": "VILNIUS",
"numOfCopiesAvailable": 4,
"address": "Saltoniškių g. 9B, LT-08126 Vilnius, Lithuania"
}
]
Requesting /api/reservation endpoint, body use FormData with parameters bookId,userId,officeId.
- Response body, status 201 (if reservation created successfully):
"reservationId": "b4ff259b-57af-4e60-aa7a-022b8d784c09",
"userId": "79ecc704-7289-426b-b0da-405d50a2e392",
"copyId": "9ae11ae5-a2c5-4a1d-bbc9-9ca00ce0d1bb",
"reservationDate": "2025-05-02T16:55:08.907+00:00",
"returned": false
- Response body, status 404 (if unable to create reservation due to unavailable copies):
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "No available copies exist for book ID: a3fdf842-f097-42b9-a0c4-4ab1ddd3c496",
"instance": "/api/reservations"