Working integration of Java, Spring Boot, JPA, Hibernate, Spring Data, Rest, H2, Unit Tests
Below you will find the 3 user stories that are implemented in this Application.
Create schema, which will allow storing different products and categories. Keep in mind that getting full category path for the product might be a requirement in the future. Currency for product price is EUR. You can choose any RDBMS.
A client should be able to perform CRUD operations for Product and Category resources. Add an ability to create products with price in different from EUR currency. Integrate the system with some open currency exchange rates API (for example http://fixer.io) Use Spring as a core framework. Use JPA/ORM Use Spring MVC. Any other technologies / libraries can be chosen on your own.
Add Spring Security support. Add a "super" user.
It's important to note that this is a HATEOAS-oriented REST API. That means you will find some URLs in the API response referencing to possible actions that can be performed on the resource.
It's highly recommendable to take a look at the unit tests defined in com.github.damiox.ecommerce.api.controller
to understand the expected API responses from a Development perspective.
Currently there are two roles defined as follows:
curl -H "Content-Type: application/json" -X POST "http://localhost:8080/login" -d '{ "username": "admin", "password": "admin" }'
curl -H "Content-Type: application/json" -X POST "http://localhost:8080/login" -d '{ "username": "user1", "password": "user1" }'
curl -H "Content-Type: application/json" -X POST "http://localhost:8080/login" -d '{ "username": "user2", "password": "user2" }'
Excepting the /login
API endpoint that is being used for authentication, all the other API endpoints require a HTTP header to be passed in as parameter in all requests.
This HTTP header will have the key name "Authorization" and the key value will be the authentication token that was retrieved during the authentication transaction (please see the Authentication
section).
Note: this token is a JWT token that is internally signed with a private key configured in the server, and it has the public user information on it.
The idea behind this JWT token is to avoid going back to the database to validate whether a JWT token is valid or not, because that information is already contained in the token itself.
There are several approaches for authentication such as Cookies, GUID tokens, OAuth2, etc... but we are choosing JWT for simplicity and scalability purposes.
Note: we are using HATEOAS-oriented REST endpoints (https://en.wikipedia.org/wiki/HATEOAS), so you will find the possible operations to perform on resources while browsing the main endpoints: /products
and /categories
In the below examples, you need to replace XXXX
with the token returned in the authentication process (please see the Authentication
section for more information).
The list of Products is always a paginated result for scalability.
URL: /products
curl -H "Authorization: XXXX" -X GET "http://localhost:8080/products"
curl -H "Authorization: XXXX" -X GET "http://localhost:8080/products/{id}"
curl -H "Content-Type: application/json" -H "Authorization: XXXX" -X POST "http://localhost:8080/products" -d '{ "name": "P1", "currency": "EUR", "price": 100.00 }'
curl -H "Content-Type: application/json" -H "Authorization: XXXX" -X PUT "http://localhost:8080/products/{id}" -d '{ "name": "P1", "currency": "EUR", "price": 100.00 }'
curl -H "Authorization: XXXX" -X DELETE "http://localhost:8080/products/{id}"
URL: /categories
curl -H "Authorization: XXXX" -X GET "http://localhost:8080/categories"
curl -H "Authorization: XXXX" -X GET "http://localhost:8080/categories/{id}"
curl -H "Content-Type: application/json" -H "Authorization: XXXX" -X POST "http://localhost:8080/categories" -d '{ "name": "C1" }'
curl -H "Content-Type: application/json" -H "Authorization: XXXX" -X PUT "http://localhost:8080/categories/{id}" -d '{ "name": "C1" }'
curl -H "Authorization: XXXX" -X DELETE "http://localhost:8080/categories/{id}"
/categories/{parentid}/subcategories/{childid}
/categories/{parentid}/subcategories
/categories/{categoryid}/products/{productid}
/categories/{parentid}/products
.
Note: the API will return also products that are being associated indirectly.
That means if a Product is associated with Category B, which is in turn a child of Category A,
then the product is directly associated with Category B, and indirectly associated with Category A.
Accessing to /categories/A/products
will return that product that is associated with Category A indirectly along with the products being associated directly with the Category A.