Category | Technology Name |
---|---|
Programming Language | Java 17 |
Project builder | Gradle 7.5.1 |
Backend Framework | Spring Boot 3.0.1 |
Testing | JUnit5, Mockito |
Database | PostgreSQL 14.1 |
Caching | Redis |
Containerization | Docker |
API Documentation | Swagger |
docker-compose up
Operation | URL | Method | Request params | Consumes | Produces | Response Status |
---|---|---|---|---|---|---|
Get publishers in page view | /api/v0/publishers | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get one publisher | /api/v0/publishers/{id} | GET | - | - | Application/JSON | 200, 404 if publisher not found |
Add new publisher | /api/v0/publishers | POST | - | Application/JSON | Application/JSON | 201, 400 if request data is invalid |
Update publisher | /api/v0/publishers/{id} | PUT | - | Application/JSON | Application/JSON | 204, 400 if request data is invalid, 404 if publisher not found |
Delete publisher | /api/v0/publishers/{id} | DELETE | - | - | Application/JSON | 204, 404 if publisher not found |
Field | Type | Description | Required | Not null |
---|---|---|---|---|
id | Long | Publisher's id | True | True |
username | String | Unique publisher's username | True | True |
String | Unique publisher's email | True | True |
This url returns the list of publishers with pagination by parameters "page" and "size". If publishers were returned, response status is 200 OK, if not, then status is 204 NO CONTENT.
GET http://localhost:8080/api/v0/publishers?page=2&size=3
{
"_embedded": {
"publishers": [
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/publishers/7"
},
"articles": {
"href": "http://localhost:8080/api/v0/users/7/articles"
},
"reviews": {
"href": "http://localhost:8080/api/v0/users/7/reviews"
}
},
"id": 7,
"username": "example-username-7",
"email": "example.email7@mail.com"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/publishers/8"
},
"articles": {
"href": "http://localhost:8080/api/v0/users/8/articles"
},
"reviews": {
"href": "http://localhost:8080/api/v0/users/8/reviews"
}
},
"id": 8,
"username": "example-username-8",
"email": "example.email8@mail.com"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/publishers/9"
},
"articles": {
"href": "http://localhost:8080/api/v0/users/9/articles"
},
"reviews": {
"href": "http://localhost:8080/api/v0/users/9/reviews"
}
},
"id": 9,
"username": "example-username-9",
"email": "example.email9@mail.com"
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/v0/publishers?page=0&size=3"
},
"prev": {
"href": "http://localhost:8080/api/v0/publishers?page=1&size=3"
},
"self": {
"href": "http://localhost:8080/api/v0/publishers?page=2&size=3"
},
"next": {
"href": "http://localhost:8080/api/v0/publishers?page=3&size=3"
},
"last": {
"href": "http://localhost:8080/api/v0/publishers?page=3&size=3"
}
},
"page": {
"size": 3,
"totalElements": 12,
"totalPages": 4,
"number": 2
}
}
200 OK / 204 NO CONTENT
This url return publisher by "id" parameter. If publisher was returned, then status is 200 OK. If not, then status will be 404 and message will be returned.
GET http://localhost:8080/api/v0/publishers/9
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/publishers/9"
},
"articles": {
"href": "http://localhost:8080/api/v0/users/9/articles"
},
"reviews": {
"href": "http://localhost:8080/api/v0/users/9/reviews"
}
},
"id": 9,
"username": "example-username-9",
"email": "example.email9@mail.com"
}
{
"message": "Publisher was not found!",
"status": 404
}
POST http://localhost:8080/api/v0/publishers
{
"id": 0,
"username": "example-username"
"email": "example-email@email.com"
}
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/publishers/1"
},
"articles": {
"href": "http://localhost:8080/api/v0/users/1/articles"
},
"reviews": {
"href": "http://localhost:8080/api/v0/users/1/reviews"
}
},
"id": 1,
"username": "example-username",
"email": "example-email@email.com"
}
201 CREATED
{
"message": "Publisher with this username already exists!",
"status": 400
}
{
"message": "Publisher with this email already exists!",
"status": 400
}
PUT http://localhost:8080/api/v0/publishers/11
{
"id": 11,
"username": "new-example-username"
"email": "new-example-email@email.com"
}
204 NO CONTENT
{
"message": "Publisher was not found!",
"status": "404"
}
DELETE http://localhost:8080/api/v0/publishers/11
204 NO CONTENT
{
"message": "Publisher was not found!",
"status": "404"
}
Operation | URL | Method | Request params | Consumes | Produces | Response Status |
---|---|---|---|---|---|---|
Get articles in page view | /api/v0/articles | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get articles of user in page view | /api/v0/users/{userId}/articles | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get articles of category in page view | /api/v0/categories/{categoryId}/articles | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get one article | /api/v0/articles/{id} | GET | - | - | Application/JSON | 200, 404 if article not found |
Add new article | /api/v0/articles | POST | - | Application/JSON | Application/JSON | 201, 400 if request data is invalid |
Update article | /api/v0/articles/{id} | PUT | - | Application/JSON | Application/JSON | 204, 400 if request data is invalid, 404 if article not found |
Delete article | /api/v0/articles/{id} | DELETE | - | - | Application/JSON | 204, 404 if publisher not found |
Field | Type | Description | Required | Not null |
---|---|---|---|---|
id | Long | Article id | True | True |
topic | String | Article topic | True | True |
description | String | Article description | True | True |
content | String | Article content | True | True |
GET http://localhost:8080/api/v0/articles?page=2&size=3
{
"_embedded": {
"publishers": [
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/7"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/7"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/7"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/7/reviews"
}
},
"id": 7,
"topic": "some-example-topic-7",
"description": "some-example-description-7",
"content": "some-example-content-7"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/8"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/8"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/8"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/8/reviews"
}
},
"id": 9,
"topic": "some-example-topic-8",
"description": "some-example-description-8",
"content": "some-example-content-8"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/9"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/9"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/9"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/9/reviews"
}
},
"id": 9,
"topic": "some-example-topic-9",
"description": "some-example-description-9",
"content": "some-example-content-9"
},
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/v0/articles?page=0&size=3"
},
"prev": {
"href": "http://localhost:8080/api/v0/articles?page=1&size=3"
},
"self": {
"href": "http://localhost:8080/api/v0/articles?page=2&size=3"
},
"next": {
"href": "http://localhost:8080/api/v0/articles?page=3&size=3"
},
"last": {
"href": "http://localhost:8080/api/v0/articles?page=3&size=3"
}
},
"page": {
"size": 3,
"totalElements": 12,
"totalPages": 4,
"number": 2
}
}
200 OK / 204 NO CONTENT
GET http://localhost:8080/api/v0/users/7/articles?page=2&size=3
{
"_embedded": {
"publishers": [
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/7"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/7"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/7"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/7/reviews"
}
},
"id": 7,
"topic": "some-example-topic-7",
"description": "some-example-description-7",
"content": "some-example-content-7"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/8"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/8"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/8"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/8/reviews"
}
},
"id": 9,
"topic": "some-example-topic-8",
"description": "some-example-description-8",
"content": "some-example-content-8"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/9"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/9"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/9"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/9/reviews"
}
},
"id": 9,
"topic": "some-example-topic-9",
"description": "some-example-description-9",
"content": "some-example-content-9"
},
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/v0/articles?page=0&size=3"
},
"prev": {
"href": "http://localhost:8080/api/v0/articles?page=1&size=3"
},
"self": {
"href": "http://localhost:8080/api/v0/articles?page=2&size=3"
},
"next": {
"href": "http://localhost:8080/api/v0/articles?page=3&size=3"
},
"last": {
"href": "http://localhost:8080/api/v0/articles?page=3&size=3"
}
},
"page": {
"size": 3,
"totalElements": 12,
"totalPages": 4,
"number": 2
}
}
200 OK / 204 NO CONTENT
GET http://localhost:8080/api/v0/categories/7/articles
{
"_embedded": {
"publishers": [
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/7"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/7"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/7"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/7/reviews"
}
},
"id": 7,
"topic": "some-example-topic-7",
"description": "some-example-description-7",
"content": "some-example-content-7"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/8"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/8"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/8"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/8/reviews"
}
},
"id": 9,
"topic": "some-example-topic-8",
"description": "some-example-description-8",
"content": "some-example-content-8"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/9"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/9"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/9"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/9/reviews"
}
},
"id": 9,
"topic": "some-example-topic-9",
"description": "some-example-description-9",
"content": "some-example-content-9"
},
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/v0/articles?page=0&size=3"
},
"prev": {
"href": "http://localhost:8080/api/v0/articles?page=1&size=3"
},
"self": {
"href": "http://localhost:8080/api/v0/articles?page=2&size=3"
},
"next": {
"href": "http://localhost:8080/api/v0/articles?page=3&size=3"
},
"last": {
"href": "http://localhost:8080/api/v0/articles?page=3&size=3"
}
},
"page": {
"size": 3,
"totalElements": 12,
"totalPages": 4,
"number": 2
}
}
200 OK / 204 NO CONTENT
POST http://localhost:8080/api/v0/articles/9
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/9"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/9"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/9"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/9/reviews"
}
},
"id": 9,
"topic": "some-example-topic-9",
"description": "some-example-description-9",
"content": "some-example-content-9"
}
200 OK
{
"message": "Article was not found!",
"status": "404"
}
POST http://localhost:8080/api/v0/articles
{
"id": 0,
"topic": "some-example-topic",
"description": "some-example-descriptiom",
"content": "some-example-content"
}
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/10"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/1"
},
"category": {
"href": "http://localhost:8080/api/v0/categories/1"
},
"reviews": {
"href": "http://localhost:8080/api/v0/articles/10/reviews"
}
},
"id": 10,
"topic": "some-example-topic",
"description": "some-example-description",
"content": "some-example-content"
}
201 CREATED
PUT http://localhost:8080/api/v0/articles
{
"id": "1",
"topic": "some-new-topic",
"description": "some-new-description",
"content": "some-new-content"
}
204 NO CONTENT
{
"message": "Article was not found!",
"status": "404"
}
DELETE http://localhost:8080/api/v0/articles
204 NO CONTENT
{
"message": "Article was not found!",
"status": "404"
}
Operation | URL | Method | Request params | Consumes | Produces | Response Status |
---|---|---|---|---|---|---|
Get categories in a page view | /api/v0/categories | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get category by id | /api/v0/categories/{id} | GET | - | - | Application/JSON | 200, 404 if category was not found |
Add new category | /api/v0/categories | POST | - | Application/JSON | Application/JSON | 200, 400 if request data is invalid |
Update existing category | /api/v0/categories/{id} | PUT | page, size | Application/JSON | - | 200, 400 if request data is invalid, 404 if category was not found |
Delete existing category | /api/v0/categories/{id} | DELETE | page, size | Application/JSON | - | 200, 404 if category was not found |
Field | Type | Description | Required | Not null |
---|---|---|---|---|
id | Long | Category id | True | True |
name | String | Unique category name | True | True |
description | String | Category description | True | True |
GET http://localhost:8080/api/v0/categories?page=2&size=3
{
"_embedded": {
"categories": [
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/categories/7"
},
"articles": {
"href": "http://localhost:8080/api/v0/categories/7/articles"
}
}
"id": 7,
"name": "some-category-name-7",
"description": some-category-description"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/categories/8"
},
"articles": {
"href": "http://localhost:8080/api/v0/categories/8/articles"
}
}
"id": 8,
"name": "some-category-name-8",
"description": some-category-description"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/categories/9"
},
"articles": {
"href": "http://localhost:8080/api/v0/categories/9/articles"
}
}
"id": 9,
"name": "some-category-name-9",
"description": some-category-description"
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/v0/categories?page=0&size=3"
},
"prev": {
"href": "http://localhost:8080/api/v0/categories?page=1&size=3"
},
"self": {
"href": "http://localhost:8080/api/v0/categories?page=2&size=3"
},
"next": {
"href": "http://localhost:8080/api/v0/categories?page=3&size=3"
},
"last": {
"href": "http://localhost:8080/api/v0/categores?page=3&size=3"
}
},
"page": {
"size": 3,
"totalElements": 12,
"totalPages": 4,
"number": 2
}
}
200 OK / 204 NO CONTENT
POST http://localhost:8080/api/v0/categories/9
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/categories/9"
},
"articles": {
"href": "http://localhost:8080/api/v0/categories/9/articles"
}
}
"id": 9,
"name": "some-category-name-9",
"description": some-category-description"
}
200 OK
{
"message": "Category was not found!",
"status": "404"
}
POST http://localhost:8080/api/v0/categories
{
"id": 0,
"name": "some-name",
"description": "some-description"
}
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/categories/10"
},
"articles": {
"href": "http://localhost:8080/api/v0/categories/10/articles"
}
}
"id": 10,
"name": "some-name",
"description": some-description"
}
201 CREATED
PUT http://localhost:8080/api/v0/categories/{id}
{
"id": 10,
"name": "some-new-name",
"description": "some-new-description"
}
204 NO CONTENT
{
"message": "Category was not found!",
"status": "404"
}
DELETE http://localhost:8080/api/v0/categories/{id}
204 NO CONTENT
{
"message": "Category was not found!",
"status": "404"
}
Operation | URL | Method | Request params | Consumes | Produces | Response Status |
---|---|---|---|---|---|---|
Get reviews made by user | /api/v0/users/{userId}/reviews | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get reviews of article | /api/v0/articles/{articleId}/reviews | GET | page, size | - | Application/JSON | 200, 204 if response is empty |
Get review by id | /api/v0/reviews/{id} | GET | - | - | Application/JSON | 200, 404 if review was not found |
Add new review | /api/v0/reviews | POST | articleId, publisherId | Application/JSON | Application/JSON | 200 |
Update existing review | /api/v0/reviews/{id} | PUT | - | Application/JSON | - | 204, 400 if request body is invalid, 404 if review was not found |
Delete existing review | /api/v0/reviews/{id} | DELETE | - | Application/JSON | - | 204, 404 if review was not found |
Field | Type | Description | Required | Not null |
---|---|---|---|---|
id | Long | Review id | True | True |
mark | Integer | Mark for article | True | True |
content | String | Content of review | True | True |
GET http://localhost:8080/api/v0/articles/1/reviews
{
"_embedded": {
"reviews": [
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/1/reviews/1"
},
"article": {
"href": "http://localhost:8080/api/v0/articles/1"
} ,
"publisher" {
"href": "http://localhost:8080/api/v0/publishers/1"
}
},
"id": 1,
"mark": 5,
"content": "some-content"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/1/reviews/2"
},
"article": {
"href": "http://localhost:8080/api/v0/articles/1"
} ,
"publisher" {
"href": "http://localhost:8080/api/v0/publishers/2"
}
},
"id": 2,
"mark": 3,
"content": "some-content"
},
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/1/reviews/3"
},
"article": {
"href": "http://localhost:8080/api/v0/articles/1"
} ,
"publisher" {
"href": "http://localhost:8080/api/v0/publishers/3"
}
},
"id": 3,
"mark": 1,
"content": "some-content"
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/v0/articles/1/reviews?page=0&size=3"
},
"prev": {
"href": "http://localhost:8080/api/v0/articles/1/reviews?page=1&size=3"
},
"self": {
"href": "http://localhost:8080/api/v0/articles/1/reviews?page=2&size=3"
},
"next": {
"href": "http://localhost:8080/api/v0/articles/1/reviews?page=3&size=3"
},
"last": {
"href": "http://localhost:8080/api/v0/articles/1/reviews?page=3&size=3"
}
},
"page": {
"size": 3,
"totalElements": 12,
"totalPages": 4,
"number": 2
}
}
200 OK / 204 NO CONTENT
GET http://localhost:8080/api/v0/reviews/1
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/1/reviews/3"
},
"article": {
"href": "http://localhost:8080/api/v0/articles/1"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/3"
}
},
"id": 3,
"mark": 1,
"content": "some-content"
}
200 OK / 204 NO CONTENT
{
"message": "Review was not found!",
"status": "404"
}
POST http://localhost:8080/api/v0/reviews
{
"id": 0,
"mark": 4,
"content": "some-content"
}
{
"_links": {
"self": {
"href": "http://localhost:8080/api/v0/articles/1/reviews/4"
},
"article": {
"href": "http://localhost:8080/api/v0/articles/1"
},
"publisher": {
"href": "http://localhost:8080/api/v0/publishers/3"
}
},
"id": 4,
"mark": 4,
"content": "some-content"
}
200 OK
POST http://localhost:8080/api/v0/reviews/4
{
"id": 4,
"mark": 5,
"content": "some-new-content"
}
204 NO CONTENT
{
"message": "Review was not found!",
"status": "404"
}
CREATE TABLE ARTICLES (
ID BIGINT NOT NULL,
CONTENT VARCHAR(255) NOT NULL,
DESCRIPTION VARCHAR(255) NOT NULL,
TOPIC VARCHAR(255) NOT NULL,
CATEGORY_ID BIGINT NOT NULL,
PUBLISHER_ID BIGINT NOT NULL,
PRIMARY KEY (ID)
)
CREATE TABLE CATEGORIES (
ID BIGINT NOT NULL,
DESCRIPTION VARCHAR(255) NOT NULL,
NAME VARCHAR(255) NOT NULL,
PRIMARY KEY (ID)
)
CREATE TABLE PUBLISHERS (
ID BIGINT NOT NULL,
EMAIL VARCHAR(255) NOT NULL,
USERNAME VARCHAR(255) NOT NULL,
PRIMARY KEY (ID)
)
CREATE TABLE REVIEWS (
ID BIGINT NOT NULL,
CONTENT VARCHAR(255) NOT NULL,
MARK INTEGER NOT NULL,
ARTICLE_ID BIGINT NOT NULL,
PUBLISHER_ID BIGINT NOT NULL,
PRIMARY KEY (ID)
)
ALTER TABLE IF EXISTS PUBLISHERS
ADD CONSTRAINT UK_USERNAME UNIQUE (username)
ALERT TABLE IF EXISTS PUBLISHERS
ADD CONSTRAINT UK_EMAIL UNIQUE (email)
ALTER TABLE IF EXISTS ARTICLES
ADD CONSTRAINT FK_CATEGORY_ID
FOREIGN KEY (CATEGORY_ID)
REFERENCES CATEGORIES
ALTER TABLE IF EXISTS ARTICLES
ADD CONSTRAINT FK_PUBLISHER_ID
FOREIGN KEY (PUBLISHER_ID)
REFERENCES PUBLISHERS
ALTER TABLE IF EXISTS REVIEWS
ADD CONSTRAINT FK_ARTICLE_ID
FOREIGN KEY (ARTICLE_ID)
REFERENCES ARTICLES
ALTER TABLE IF EXISTS REVIEWS
ADD CONSTRAINT FK_PUBLISHER_ID
FOREIGN KEY (PUBLISHER_ID)
REFERENCES PUBLISHERS