Skip to content

NormantasN/books-api

Repository files navigation

Books


On this page


Local requirements

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.

Code style configuration:

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.

Running PMD & Spotbugs with local profile:

SPRING_PROFILES_ACTIVE=local ./gradlew check


How to setup local database?

Using Docker:

Run the following command to start the application locally

docker-compose up -d

Check if container is running:

docker ps

Troubleshooting if container won't start:

docker logs books-db

If needed, remove and recreate

docker rm -f books-db

To stop the container:

docker stop books-db

To start it again:

docker start books-db


How do I start up the application?

Make sure database container is running (use any terminal):

docker ps

If container with name:"books-db" not found repeat steps in How to setup local database?

1. Open the application in your IDE

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+M and in opened context menu find and select Active profile
  • Apply and from now by default using short-cut:Shift+F10 application going to start using application-local.properties

2. Navigate using terminal to project main directory

Run this command:

./gradlew bootRun --args='--spring.profiles.active=local'

Note

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.


Liquibase

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.


Image API endpoint description


Here is /api/image endpoint to call request with GET (download) and POST (upload) image methods.

1. Use case of GET request.

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"

2. Use case of POST request

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"

Authorize endpoints

When creating endpoints make sure they are authorized

Add @PreAuthorize("isAuthenticated()") annotation to endpoint.

Note: "isAuthenicated" can later be changed to role-based authentication.

When making controller tests

After get(BASE_URL) make sure to add .with(jwtAuthentication()).

This will get authentication from Security Context.


Book endpoints description

1. GET all books.

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"
    }

2. GET book by ID ( UUID ).

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
        }
    ]  
}

3. GET all offices and availability for a book by ID ( UUID ).

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"
    }
]

Reservation endpoint description

POST request

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"

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 6