This repository contains the code for the backend of the full-stack Issue Tracker project.
The frontend code can be found in the following repository.
- Languages: Java 19
- Frameworks: Spring/Spring Boot
- Build tools: Maven
- Servers: Tomcat
- Databases: PostgreSQL, Redis
- Spring Web
- Spring Data JPA
- Spring Security
- Spring HATEOAS
- PostgreSQL Driver
- Spring Boot Dev Tools
- Spring Validation
- Lombok
- Jackson Databind
- SpringDoc OpenAPI WebMVC UI
- Mapstruct
- JJWT
- Spring Data Redis
- Jedis
- Spring Security ACL
- RESTful CRUD API
- Authentication & authorization (JWT)
- Fine-grained resource access control
- OpenAPI documentation with Swagger UI playground
You can acess the OpenAPI Specification of the Issue Tracker API in JSON format at http://localhost:8080/v3/api-docs.
You can also download the documentation as a YAML file from http://localhost:8080/v3/api-docs.yaml.
You can explore and play with the Issue Tracker API in an interactive environment generated by Swagger UI at http://localhost:8080/swagger-ui.html.
You need to have PostgreSQL and Redis servers set up and running in order to run this application.
git clone https://github.com/akorotky/issue-tracker-api.git
cd issue-tracker-api
In the application.properties
file, put your PostgreSQL credentials. You can also add or remove other properties if you like.
You can run the application using an IDE of your choice (i.e. IntelliJ), or with the command line mvn spring-boot:run
(you need to have Maven installed). The application server will start at http://localhost:8080.
Since the application is secured, you need to authenticate first in order to access the API. You can do that using Postman (best experience) or using the Linux curl CLI tool.
A mock user with username user
and password password
already exists, so you can use these credentials to authenticate. To do that, you need to send a HTTP POST request to http://localhost:8080/api/auth/login
with a valid username and password.
{
"username": "user",
"password": "password"
}
{
"accessToken": "eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiYXV0aG9yaXRpZXMiOlsiVVNFUiJdLCJpYXQiOjE2ODQ0NjM5MzQsImV4cCI6MTY4NDQ2NDgzNH0.LLkEf3QfkYWr_gOxt4jwCTg5leXqy85BMb5VqrIfJS0ma2RqZVyfaGE3Tlckt5CdKRTSdvUv3eUeMWlrjDwspA",
"refreshToken": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiYXV0aG9yaXRpZXMiOlsiVVNFUiJdLCJpYXQiOjE2ODQ0NjM5MzQsImV4cCI6MTY4NTA2ODczNH0.qfttGoaNLTaG17OGIncP5S3_D9sGLvaw6pDwCtlDY8oqQULuGKm3FH7xHIZjkTUjOZP71xXWXAIXCkw94x_mNIAt5uK59_ZnY-vce5sj03C0VGQBHfIzZsfk_di9K8uH7rd6HckGizl_vG4kUgUpnmzER5qN97G0_D-7f1kRToyO1ud6_X7phd7bN1PCqJBdBYm4KcV3lP3_gVmNvV-upahTSlR5XTYX4DZu828DGVemYZed72UN7kazvd4pXwQ1wwcR9T7JEo3pexZpoYB5Mnhgq1hW6iWEbX9P6RoOHQOIafOy1E9lF76Jh_nMCe65cAw-a7ErZsHg-aLn3V4vGw"
}
Example using curl: curl -X POST -H http://localhost:8080/api/auth/login "Content-Type: application/json" -d '"username":"user", "password":"password"'
Access token is a short-lived Json Web Token (the token is valid only for 15 minutes) that you can put into the HTTP Authorization header when accessing the API, in order to avoid always sending username and password on every request. When the access token expires, you can use the refresh token (valid for 1 week) to generate a new access token by sending a POST request to the http://localhost:8080/api/auth/token
endpoint with the refresh token in the request body.
HTTP Method | URI | Description |
---|---|---|
POST | /api/auth/login | Sign in |
POST | /api/auth/token | Create and get an access + refresh token pair |
HTTP Method | URI | Description |
---|---|---|
GET | /api/users | Get all users |
POST | /api/users | Create/register a user |
GET | /api/users/{username} | Get user data |
PATCH | /api/users/{username} | Update user data |
DELETE | /api/users/{username} | Delete user |
HTTP Method | URI | Description |
---|---|---|
GET | /api/projects | Get all projects |
GET | /api/projects?user={username} | Get all projects where user is the owner |
GET | /api/projects?collaborator={username} | Get all projects where user is a collaborator |
POST | /api/projects | Create a project |
GET | /api/projects/{projectId} | Get project data |
PATCH | /api/projects/{projectId} | Update project data |
DELETE | /api/projects/{projectId} | Delete project |
DELETE | /api/projects/{projectId}/issues | Get project issues |
GET | /api/projects/{projectId}/collaborators | Get project collaborators |
PUT | /api/projects/{projectId}/collaborators/{username} | Add a project collaborator |
DELETE | /api/projects/{projectId}/collaborators/{username} | Delete project collaborator |
HTTP Method | URI | Description |
---|---|---|
GET | /api/issues | Get all issues |
POST | /api/issues | Create an issue |
GET | /api/issues/{issueId} | Get issue data |
PATCH | /api/issues/{issueId} | Update issue |
DELETE | /api/issues/{issueId} | Delete issue |
GET | /api/issues/{issueId}/comments | Get issue comments |
HTTP Method | URI | Description |
---|---|---|
GET | /api/comments | Get all comments |
POST | /api/comments | Create a comment |
GET | /api/comments/{commentId} | Get comment data |
PATCH | /api/comments/{commentId} | Update comment |
DELETE | /api/comments/{commentId} | Delete comment |
A GET request to http://localhost:8080/api/projects/1
returns the project with an id of 1:
{
"id": 1,
"title": "Issue Tracker API",
"description": "RESTful API for project collaboration and issue tracking."
"private": false,
"owner": {
"id": 1,
"username": "user",
"email": "user@issuetracker.test",
"roles": [
"USER"
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/projects/1"
},
"owner": {
"href": "http://localhost:8080/api/users/user"
},
"collaborators": {
"href": "http://localhost:8080/api/projects/1/collaborators"
},
"issues": {
"href": "http://localhost:8080/api/projects/1/issues"
}
}
}