The Products API is a REST service built using Spring boot and ReactJS. It allows basic CRUD operations on 3 main entities: Products, Currencies and Price points.
Maven 3.3.9+
- Clone the project to a folder on your machine.
cd
to the project folder and run$ mvn spring-boot:run
- In a browser navigate to http://localhost:8080/react
You should see the following screen:
The application uses an in memory H2 database that is loaded with dummy data upon start up.
The 3 main entities in the service are Products, Currencies and Price points. Each Product can have multiple Price points and each Price point is linked to a Currency. Currently only a single price point per currency is allowed for each Product using a unique composite database index on the product_id and currency_id.
The service is covered by end-to-end tests for each resource. To run the tests simply run mvn test
.
Each Test class covers the 5 basic CRUD operations including creating a resource, updating a resource, deleting a resource, retreiving a single and a collection of resources in addition to a couple of invalid inputs to resource creation.
The api uses HATEOS as a standard for the REST response. The initial discovery point is simply /
.
$ curl http://localhost:8080/
{
"_links": {
"price-point": {
"href": "http://localhost:8080/price-points{?page,size,sort,projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/currencies{?page,size,sort}",
"templated": true
},
"product": {
"href": "http://localhost:8080/products{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile"
}
}
}
Response code: 200
$ curl http://localhost:8080/products/1
{
"name": "dummy product",
"description": "A dummy product for testing purposes.",
"tags": "tag1,tag2,tag3",
"_embedded": {
"pricePoints": [
{
"currency": {
"name": "Dollars",
"iso3": "USD"
},
"price": 1.23,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/1{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/1/currency"
},
"product": {
"href": "http://localhost:8080/price-points/1/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/products/1"
},
"product": {
"href": "http://localhost:8080/products/1"
},
"pricePoints": {
"href": "http://localhost:8080/products/1/pricePoints"
}
}
}
Response code: 200
$ curl http://localhost:8080/products
{
"_embedded": {
"product": [
{
"name": "dummy product",
"description": "A dummy product for testing purposes.",
"tags": "tag1,tag2,tag3",
"_embedded": {
"pricePoints": [
{
"currency": {
"name": "Dollars",
"iso3": "USD"
},
"price": 1.23,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/1{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/1/currency"
},
"product": {
"href": "http://localhost:8080/price-points/1/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/products/1"
},
"product": {
"href": "http://localhost:8080/products/1"
},
"pricePoints": {
"href": "http://localhost:8080/products/1/pricePoints"
}
}
},
{
"name": "second dummy product",
"description": "A second dummy product for testing purposes.",
"tags": "tag4",
"_embedded": {
"pricePoints": [
{
"currency": {
"name": "Dollars",
"iso3": "USD"
},
"price": 11.11,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/2{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/2/currency"
},
"product": {
"href": "http://localhost:8080/price-points/2/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/products/2"
},
"product": {
"href": "http://localhost:8080/products/2"
},
"pricePoints": {
"href": "http://localhost:8080/products/2/pricePoints"
}
}
},
{
"name": "third dummy product",
"description": "A third dummy product for testing purposes.",
"tags": null,
"_links": {
"self": {
"href": "http://localhost:8080/products/3"
},
"product": {
"href": "http://localhost:8080/products/3"
},
"pricePoints": {
"href": "http://localhost:8080/products/3/pricePoints"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/products"
},
"profile": {
"href": "http://localhost:8080/profile/products"
}
},
"page": {
"size": 20,
"totalElements": 3,
"totalPages": 1,
"number": 0
}
}
Response code: 201
$ curl -X POST -H "Content-Type: application/json" -d '{"name":"dummy product","description":"A dummy product for testing purposes.","tags":"tag1,tag2,tag3"}' http://localhost:8080/products
{
"name": "dummy product",
"description": "A dummy product for testing purposes.",
"tags": "tag1,tag2,tag3",
"_links": {
"self": {
"href": "http://localhost:8080/products/4"
},
"product": {
"href": "http://localhost:8080/products/4"
},
"pricePoints": {
"href": "http://localhost:8080/products/4/pricePoints"
}
}
}
Response code: 200
$ curl -X PUT -H "Content-Type: application/json" -d '{"name":"dummy product","description":"A dummy product for testing purposes.","tags":"tag1,tag2,tag3"}' http://localhost:8080/products/1
{
"name": "new product",
"description": "new product for testing purposes.",
"tags": "tag4",
"_embedded": {
"pricePoints": [
{
"currency": {
"name": "Dollars",
"iso3": "USD"
},
"price": 1.23,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/1{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/1/currency"
},
"product": {
"href": "http://localhost:8080/price-points/1/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/products/1"
},
"product": {
"href": "http://localhost:8080/products/1"
},
"pricePoints": {
"href": "http://localhost:8080/products/1/pricePoints"
}
}
}
Response code: 204
$ curl -X DELETE http://localhost:8080/products/1
Response code: 200
$ curl http://localhost:8080/currencies/1
{
"name": "Dollars",
"iso3": "USD",
"_links": {
"self": {
"href": "http://localhost:8080/currencies/1"
},
"currency": {
"href": "http://localhost:8080/currencies/1"
}
}
}
Response code: 200
$ curl http://localhost:8080/currencies
{
"_embedded": {
"currency": [
{
"name": "Dollars",
"iso3": "USD",
"_links": {
"self": {
"href": "http://localhost:8080/currencies/1"
},
"currency": {
"href": "http://localhost:8080/currencies/1"
}
}
},
{
"name": "British Pound",
"iso3": "GBP",
"_links": {
"self": {
"href": "http://localhost:8080/currencies/2"
},
"currency": {
"href": "http://localhost:8080/currencies/2"
}
}
},
{
"name": "Euro",
"iso3": "EUR",
"_links": {
"self": {
"href": "http://localhost:8080/currencies/3"
},
"currency": {
"href": "http://localhost:8080/currencies/3"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/currencies"
},
"profile": {
"href": "http://localhost:8080/profile/currencies"
},
"search": {
"href": "http://localhost:8080/currencies/search"
}
},
"page": {
"size": 20,
"totalElements": 3,
"totalPages": 1,
"number": 0
}
}
Response code: 201
$ curl -X POST -H "Content-Type: application/json" -d '{"name":"dollars","iso3":"USD"}' http://localhost:8080/currencies
{
"name": "dollars",
"iso3": "USD",
"_links": {
"self": {
"href": "http://localhost:8080/currencies/4"
},
"currency": {
"href": "http://localhost:8080/currencies/4"
}
}
}
Response code: 200
$ curl -X PUT -H "Content-Type: application/json" -d '{"name":"new currency","iso3":"NEW"}' http://localhost:8080/currencies/1
{
"name": "new currency",
"iso3": "NEW",
"_links": {
"self": {
"href": "http://localhost:8080/currencies/1"
},
"currency": {
"href": "http://localhost:8080/currencies/1"
}
}
}
Response code: 204
$ curl -X DELETE http://localhost:8080/currencies/1
Response code: 200
$ curl http://localhost:8080/price-points/1
{
"price": 1.23,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/1"
},
"pricePoint": {
"href": "http://localhost:8080/price-points/1{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/1/currency"
},
"product": {
"href": "http://localhost:8080/price-points/1/product"
}
}
}
Response code: 200
$ curl http://localhost:8080/price-points
{
"_embedded": {
"price-point": [
{
"currency": {
"name": "Dollars",
"iso3": "USD"
},
"price": 1.23,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/1"
},
"pricePoint": {
"href": "http://localhost:8080/price-points/1{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/1/currency"
},
"product": {
"href": "http://localhost:8080/price-points/1/product"
}
}
},
{
"currency": {
"name": "Dollars",
"iso3": "USD"
},
"price": 11.11,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/2"
},
"pricePoint": {
"href": "http://localhost:8080/price-points/2{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/2/currency"
},
"product": {
"href": "http://localhost:8080/price-points/2/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/price-points"
},
"profile": {
"href": "http://localhost:8080/profile/price-points"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
Response code: 201
$ curl -X POST -H "Content-Type: application/json" -d '{"currency":"http://localhost:8080/currencies/1","product":"http://localhost:8080/products/1","price":"555"}' http://localhost:8080/price-points
{
"price": 111,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/3"
},
"pricePoint": {
"href": "http://localhost:8080/price-points/3{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/3/currency"
},
"product": {
"href": "http://localhost:8080/price-points/3/product"
}
}
}
Response code: 200
$ curl -X PUT -H "Content-Type: application/json" -d '{"currency":"http://localhost:8080/currencies/1","product":"http://localhost:8080/products/3","price":"111"}' http://localhost:8080/price-points/1
{
"price": 111,
"_links": {
"self": {
"href": "http://localhost:8080/price-points/5"
},
"pricePoint": {
"href": "http://localhost:8080/price-points/5{?projection}",
"templated": true
},
"currency": {
"href": "http://localhost:8080/price-points/5/currency"
},
"product": {
"href": "http://localhost:8080/price-points/5/product"
}
}
}
Response code: 204
$ curl -X DELETE http://localhost:8080/price-points/1
$ mvn package
$ java -jar target/productapi-0.0.1-SNAPSHOT.jar
Sherif Allam
This project is licensed under the MIT License.