Skip to content

LibraryProjectGroup/library-project-backend

Repository files navigation

library-project-backend

Installing the project

You need to have npm installed to run this project, you can check if you have it installed by running npm -v in the command line.

Clone the repository on your computer. Detailed instructions can be found here.

Environment variables

Database connection doesn't work without .env in root folder. .env is set to be ignored by git with .gitignore, so create one locally. By default the backend server will start on port 3000, which can be changed by setting PORT environment variable. Make sure backend and frontend are using different ports.

Here's an example of a .env file you can use:

#Backend
DATABASE_SERVER=
DATABASE_NAME=
DATABASE_USER=
DATABASE_PASSWORD=
OIDC_AUTH_BACKLINK_URL=
OIDC_AUTH_REDIRECT_URL=
PORT=
#MySQL
MYSQL_DATABASE=
MYSQL_USER=
MYSQL_PASSWORD=
MYSQL_ROOT_PASSWORD=
MYSQL_PORT=

How to run

You will need to run the backend application and the database. You can have them separately, or you can use docker-compose.

Using docker-compose

Database initialization is handled automatically on creation using the *.sql files in the /sql folder.

If you have Windows, make sure you have Docker Desktop running before running the command. https://www.docker.com/products/docker-desktop/

For development

docker-compose -f docker-compose-dev.yml up -d

For staging

docker-compose -f docker-compose-staging.yml up -d

For tests

docker-compose -f docker-compose-test.yml up -d

The -f flag specifies the file since there are different ones. The -d flag (detach) runs the container in the background allowing you to close the terminal without killing the process.

Running the node project

Use npm ci or npm install to install node modules.

Start the backend by running npm start.


Workflows

There are two workflow files that run on this repository: node.js.yml and deploy-staging.yml.

The Node.js CI workflow is designed to ensure code quality and functionality before changes are merged. It triggers on every push or when a pull request is made to development, main, or s23-staging branches. Here's a step-by-step breakdown of the workflow:

  1. Code Formatting Check: Utilizes Prettier to ensure code is following the project's formatting standards.
  2. Backend Spin-up: Initiates the backend and employs wait-on to verify when the URL is available. A timeout here often indicates an issue with container/app build.
  3. Robot Tests: Executes robot tests to verify code functionality. If tests fail, review and update your code or the tests accordingly.

The Prettier workflow

The Prettier workflow is set up in the code with Pretty-quick (https://github.com/azz/pretty-quick) and Husky (https://typicode.github.io/husky/) to ensure consistent formatting. Pretty-Quick checks formatting when a developer tries to commit to the repo, and fixes formatting using our prettier config (.prettierrc.json file in the root of the project). Husky ensures that the pre-commit checks works with a pre-commit hook. Make sure you have all dependencies installed by running: npm install before you start. In your code editor, you can also set prettier checks on save, which will help the process.

Endpoints

Note #4:

Endpoints that use body will be in JSON format. Endpoint requires either query or body, if query is present, the body section will not be shown and vice versa.

Postman files

If not separately mentioned, On Success Response schema is:

{
"ok": true
}

If not separately mentioned, On Fail Response schema is:

{
"ok": false
}

Auth

Show Endpoints

/auth/register (POST)

Body

{
  "username": string,
  "email": string,
  "password": string
}

On Success Response schema:

{
  "ok": true,
  "secret": string
}

On Fail Response schema:

{
  "ok": false,
  "message": string
}

/auth/login (POST)

Body

{
  "email": string,
  "password": string
}

On Success Response schema:

{
  "ok": true,
  "userId": number,
  "secret": string
}

On Fail Response schema:

{
  "ok": false,
  "message": string
}

/auth/logout (POST)

On Fail Response schema:

{
  "ok": false,
  "message": string
}

Book

Show Endpoints

/book/all (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "title": string,
    "image": string,
    "author": string,
    "year": number,
    "topic": string,
    "isbn": string,
    "deleted": boolean,
    "homeOfficeId": number,
    "homeOfficeName": string,
    "homeOfficeCountry": string
  }
]

/book/page?page={page}&pageSize={pageSize} (GET)

pageSize is optional

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "title": string,
    "image": string,
    "author": string,
    "year": number,
    "topic": string,
    "isbn": string,
    "deleted": boolean,
    "homeOfficeId": number,
    "homeOfficeName": string,
    "homeOfficeCountry": string
  }
]

/book/count (GET)

On Success Response schema:

number

/book?id={id} (GET)

On Success Response schema:

{
  "id": number,
  "library_user": number,
  "title": string,
  "image": string,
  "author": string,
  "year": number,
  "topic": string,
  "isbn": string,
  "deleted": boolean,
  "homeOfficeId": number,
  "homeOfficeName": string,
  "homeOfficeCountry": string
}

/book?id={id} (DELETE)

On Success Response schema:

{
  "id": number,
}

/book (POST)

Body:

{
  "library_user": number,
  "title": string,
  "image": string,
  "author": string,
  "year": number,
  "isbn": string,
  "topic": string,
  "homeOfficeId": string
}

/book (PUT)

Body:

{
  "id": number,
  "title": string,
  "image": string,
  "author": string,
  "year": number,
  "isbn": string,
  "topic": string,
  "homeOfficeId": string,
}

/book/all/reserved (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "title": string,
    "image": string,
    "author": string,
    "year": number,
    "topic": string,
    "isbn": string,
    "deleted": boolean,
    "homeOfficeId": number,
    "homeOfficeName": string,
    "homeOfficeCountry": string
  }
]

Book List

Show Endpoints

/booklist/all (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "name": string
  }
]

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklist/user (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "name": string
  }
]

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklist/books?id={id} (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "title": string,
    "image": string,
    "author": string,
    "year": number,
    "topic": string,
    "isbn": string,
    "deleted": boolean,
    "homeOfficeId": number,
    "homeOfficeName": string,
    "homeOfficeCountry": string
  }
]

/booklist/info?id={id} (GET)

On Success Response schema:

{
  "userId": number,
  "username": string,
  "name": string
}

/booklist?id={id} (GET)

On Success Response schema:

{
  "id": number,
  "library_user": number,
  "name": string
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklist (PUT)

Body:

{
  "id": number,
  "name": string
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklist (POST)

Body:

{
  "id": number,
  "name": string
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklist (DELETE)

Body:

{
  "id": number
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

Book List Entry

Show Endpoints

/booklistentry/all (GET)

On Success Response schema:

[
  {
    "id": number,
    "list": number,
    "book": number
  }
]

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklistentry/list?id={id} (GET)

On Success Response schema:

[
  {
    "id": number,
    "list": number,
    "book": number
  }
]

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklistentry?id={id} (GET)

On Success Response schema:

{
  "id": number,
  "list": number,
  "book": number
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklistentry (POST)

Body:

{
  "list": number,
  "book": number
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklistentry (DELETE)

Body:

{
  "id": number
}

On Fail Response schema:

{
  "ok": false,
  "status": 500
}

/booklistentry/book (DELETE)

Body:

{
  "listId": number,
  "bookId": number
}

Book Request

Show Endpoints

/bookrequest/all (GET)

On Success Response schema:

[
  {
    "id": number,
    "userId": number,
    "isbn": string,
    "title": string,
    "reason": string,
    "status": Book_request_status
  }
]

/bookrequest (POST)

Body:

{
  "userId": number,
  "isbn": string,
  "title": string,
  "reason": string
}

/bookrequest/updatestatus (PUT)

Body:

{
  "id": number,
  "status": Book_request_status
}

Book Reservation

Show Endpoints


Bookreservations are considered active, if they are not canceled, loaned, and the connected Borrow hasn't been returned more than RESERVATION_DAYS prior to now. Book_reservations are considered loanable, if they are active and the connecting Borrow has been returned. Book_reservation status isn't automatically updated in the backend, but is instead filtered through SQL queries and _filterActiveReservations function in queries/book_reservation.

/bookreservation/all (GET)

On Success Response schema:

[
  {
    "id": number,
    "userId": number,
    "bookId": number,
    "borrowId": number,
    "reservationDatetime": Date,
    "loaned": boolean,
    "canceled": boolean
  }
]

/bookreservation/all/current (GET)

On Success Response schema:

[
  {
    "id": number,
    "userId": number,
    "bookId": number,
    "borrowId": number,
    "reservationDatetime": Date,
    "loaned": boolean,
    "canceled": boolean,
    "returnDate": Date | null
  }
]

/bookreservation/active/loanable

[
  {
    "id": number,
    "bookId": number,
    "reservationDatetime": Date,
    "loaned": boolean,
    "canceled": boolean,
    "returnDate": Date
  }
]

/bookreservation/all/extended (GET)

On Success Response schema:

[
  {
    "id": number,
    "username": string,
    "image": string,
    "title": string,
    "bookId": number,
    "reservationDatetime": Date,
    "loaned": boolean,
    "canceled": boolean,
    "returnDate": Date | null
  }
]

/bookreservation/book (GET)

Body:

{
  "bookId": number
}

On Success Response schema:

[
  {
    "id": number,
    "userId": number,
    "bookId": number,
    "borrowId": number,
    "reservationDatetime": Date,
    "loaned": boolean,
    "canceled": boolean,
    "returnDate": Date | null
  }
]

/bookreservation (POST)

Body:

{
  "userId": number,
  "bookId": number
}

/bookreservation/cancel (POST)

Body:

{
  "bookId": number
}

/bookreservation/loan (POST)

Body:

{
  "reservationId": number
}

/bookreservation/user/current (POST)

Body:

{
  "userId": number,
}

BookReview

Show Endpoints

/review/all (GET)

On Success Response schema:

[
{
        "id": number,
        "user_id": number,
        "book_id": number,
        "comment": string,
        "rating": number,
        "review_date": Date
}
]

/review/book (GET)

Body:

{
  "bookId": number
}

On Success Response schema:

[
{
        "id": number,
        "user_id": number,
        "book_id": number,
        "comment": string,
        "rating": number,
        "review_date": Date
}
]

/review/average (GET)

Body:

{
  "bookId": number
}

On Success Response schema:

{
    "averageRating": number
}

/review (DELETE)

Body:

{
    "reviewId": number
}

/review (POST)

Body:

{
    "bookId": number,
    "comment": string,
    "rating": number
}

/review (POST)

Body:

{ 
    "comment": string,
    "rating": number,
    "reviewId": number
}

Borrow

Show Endpoints

/borrow/all (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "book": number,
    "dueDate": Date,
    "borrowDate": Date,
    "returned": boolean,
    "returnDate": Date | null
  }
]

/borrow?id={id} (GET)

On Success Response schema:

{
  "id": number,
  "library_user": number,
  "book": number,
  "dueDate": Date,
  "borrowDate": Date,
  "returned": boolean,
  "returnDate": Date | null
}

/borrow (DELETE)

Body:

{
    "id": number
}

/borrow (POST)

Body:

{
  "userId": number,
  "bookId": number,
  "dueDate": Date,
  "borrowDate": Date,
  "returned": boolean
}

On Fail Response schema:

{
  "ok": false,
  "message": string
}

/borrow (PUT)

Body:

{
  "id": number,
  "library_user": number,
  "book": number,
  "dueDate": Date,
  "borrowDate": Date,
  "returned": boolean,
  "returnDate": Date | null
}

/borrow/current (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "book": number,
    "dueDate": Date,
    "borrowDate": Date,
    "returned": boolean,
    "returnDate": Date | null
  }
]

/borrow/expired/admin (GET)

On Success Response schema:

[
  {
    "borrowId": number,
    "dueDate": Date,
    "title": string,
    "bookId": number,
    "username": string,
    "userId": number
  }
]

/borrow/current/admin (GET)

On Success Response schema:

[
  {
    "username": string,
    "title": string,
    "borrowDate": Date,
    "dueDate": Date,
    "id": number
  }
]

/borrow/expired (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "book": number,
    "dueDate": Date,
    "borrowDate": Date,
    "returned": boolean,
    "returnDate": Date | null
  }
]

/borrow/session (GET)

On Success Response schema:

[
  {
    "id": number,
    "library_user": number,
    "book": number,
    "dueDate": Date,
    "borrowDate": Date,
    "returned": boolean,
    "returnDate": Date | null
  }
]

/borrow/return (PUT)

Body:

{
  "id": number,
  "library_user": number,
  "book": number,
  "dueDate": Date,
  "borrowDate": Date,
  "returned": boolean,
  "returnDate": Date | null
}

FavoriteBooks

Show Endpoints

/favorite/check (GET)

Body:

{
  "bookId": number
}

On Success Response schema:

{
    "isFavorited": boolean
}

/favorite/count (GET)

Body:

{
  "bookId": number
}

On Success Response schema:

{
    "count": number
}

/favorite (DELETE)

Body:

{
  "bookId": number
}

/favorite (POST)

Body:

{
    "bookId": number 
}

Health

Show Endpoints

/health (GET)

On Fail Response schema:

{
  "ok": false,
  "error:": string
}

Office

Show Endpoints

/office/all (GET)

Body:

{
  "id": number,
  "name": string,
  "countryCode": string
}

/office/homeOfficeId (GET)

Body:

{
  "id": number,
  "name": string,
  "countryCode": string
}

/office/homeOfficeId (DELETE)

Body:

{
  "id": number
}

/office/homeOfficeId (PUT)

Body:

{
  "id": number,
  "name": string,
  "countryCode": string
}

Password Reset

Show Endpoints

/passwordreset/secret?id={id} (GET)

On Success Response schema:

{
  "ok": true,
  "secret": string
}

/passwordreset (POST)

Body:

{
  "id": number,
  "username": string,
  "email": string,
  "passw": string,
  "administrator": boolean,
  "homeOfficeId?": number
}

On Fail Response schema:

{
  "ok": false,
  "message": string
}

User

Show Endpoints

/user/all (GET)

On Success Response schema:

[
  {
  "id": number,
  "username": string,
  "email": string,
  "administrator": boolean,
  "deleted": boolean,
  "homeOfficeId?": number
  }
]

/user?id={id} (GET)

On Success Response schema:

{
  "id": number,
  "username": string,
  "email": string,
  "administrator": boolean
}

/user/session (GET)

On Success Response schema:

{
  "id": number,
  "username": string,
  "email": string,
  "administrator": boolean,
  "deleted": boolean
}

/user (DELETE)

Body:

{
  "id": number
}

/user (POST)

Body:

/user?username={username}&email={email}&password={password}&administrator={administrator}

/user (PUT)

/user?id={id}&username={username}&email={email}&password={password}&administrator={administrator} (PUT)

/user (PUT)

/user/admin?id={id}&username={username}&email={email}&administrator={administrator} (PUT)

Database Documentation

Database Diagram and Documentation