This document describes a system which allows users to manage shopping lists on their mobile phone. The system has the two main parts - the mobile application (frontend) and server (backend). According to the code challenge task this project implements only server-backend.
To compile and run this project you need any operating system with bash interpreter or Windows 2000 or newest. Also you need Oracle JDK or OpenJDK version 8.
In the project folder type the following command:
Windows:
shopping-list> mvnw.cmd clean install
Linux / MacOS:
$ ./mvnv clean install
In the project folder type the following command:
For all operating systems:
java -jar ./target/shopping-list*.jar
The backend solution enables mobile app to synchronize lists and its items across devices and accounts. The system can be in use cases shown on the diagram:
This project written in Java 8 and uses and the following tools:
-
Spring Boot - provides the ability to create uber-jar, all-in-one file
-
Spring Data JPA - a convenient way to operate data
-
Hibernate - the ORM engine
-
Spring security - to protect private user date from unauthorized access
-
Jersey - JAX-RS implementation for creating RESTful services
-
H2 - SQL-compatible in-memory database
-
HikariCP - a connection pool for optimal database connections using
-
Junit - testing framework
-
Lombok - a library to reduce boilerplate code
-
Maven - a compile tool
The project contains following packages:
-
entities - contains entities that are serialized to json structures
-
exceptions - contains specific exceptions
-
repository - contains interfaces to manage data in database
-
rest - contains
/lists
and/users
(not implemented in current version) endpoint with data-classes and jersey config -
security - security config classes
-
services - contains the application main logic to process users (not implemented in current version), shopping lists, and list items.
The file application.properties contains app settings.
The test folder contains tests for all implemented in the main code use cases.
data.sql contains test data imported into database on tests start.
The backend solution built as RESTful web-service and has the following endpoints:
-
/users - used for manage users (not implemented in current version)
-
/lists - used for manage shopping lists
-
GET /lists
- returns all lists with items for an authorized user and200 "ok"
response code. -
GET /list/{id}
- returns specific list with items by given id. Returns200 "ok"
response code in case of success or404 not found
if the list doesn’t exist or doesn’t belong to the authorized user. -
POST /list
- stores a new list with items. Returns200 "ok"
response code and URL to the new list in case of success. If the list is not correct, returns400 "bad request"
response code. Doesn’t return a body. -
POST /list/{id}
- replaces an existing list with given id by modified one. Returns response code200 "ok"
if no errors or400 "bad request"
if incoming data is not correct. Returns404 not found
if the list doesn’t exist or doesn’t belong to the authorized user. In the body returns information about conflicts if they were happening. -
DELETE /list/{id}
- removes a shopping list with given id from the database. Returns200 "ok"
if success or404 not found
if the list doesn’t exist or doesn’t belong to the authorized user. Response has no body.
-
Method: GET
URI: /lists/
Request body: <ignored>
Response body example:
[
{
"id": 1,
"name": "CPUs",
"description": "Processors for servers #1, #6, #11",
"owner": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"listItems": [
{
"id": 1,
"version": 0,
"name": "Core i5",
"description": "CPU Intel Core i5",
"checked": true,
"quantity": 2,
"isDeleted": false,
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
}
},
{
"id": 2,
"version": 0,
"name": "Core i7",
"description": "CPU Intel Core i7",
"checked": true,
"quantity": 1,
"isDeleted": false,
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
}
}
]
},
{
"id": 5,
"name": "RAMs",
"description": "Memory for servers",
"owner": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"listItems": []
}
]
Method: GET
URI example: /lists/1
Basic authorization: Required
Request body: <ignored>
Response body example:
{
"id": 1,
"name": "CPUs",
"description": "Processors for servers #1, #6, #11",
"owner": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"listItems": [
{
"id": 1,
"version": 0,
"name": "Core i5",
"description": "CPU Intel Core i5",
"checked": true,
"quantity": 2,
"isDeleted": false,
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
}
},
{
"id": 2,
"version": 0,
"name": "Core i7",
"description": "CPU Intel Core i7",
"checked": true,
"quantity": 1,
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
}
}
]
}
Method: POST
URI: /lists/
Basic authorization: Required
Request body example:
{
"name": "CPUs",
"description": "Processors for servers #1, #6, #11",
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
},
"listItems": [
{
"version": 0,
"name": "Core i5",
"description": "CPU Intel Core i5",
"checked": true,
"quantity": 2
},
{
"version": 0,
"name": "Core i7",
"description": "CPU Intel Core i7",
"checked": true,
"quantity": 1
}
]
}
Response body: <empty>
Method: POST
URI example: /lists/1
Basic authorization: Required
Request body example (no info about conflicts):
{
"id": 1,
"name": "CPUs",
"description": "Processors for servers #1, #6, #11",
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
},
"listItems": [
{
"id": 1,
"version": 0,
"name": "Core i5",
"description": "CPU Intel Core i5",
"checked": true,
"quantity": 2
},
{
"id": 2,
"version": 1,
"isDeleted": true
}
]
}
Response body (no conflicts):
{
"conflictedItems": [],
"newRemoteItems": []
}
Response body (has 1 conflict):
{
"conflictedItems": [
{
"id": 1,
"version": 2,
"name": "AMD",
"description": "CPU AMD",
"checked": true,
"quantity": 1,
"author": {
"id": 2,
"login": "[email protected]",
"name": "Miranda Johnson"
},
"device": {
"id": 3,
"name": "Apple iPhone"
},
}
],
"newRemoteItems": []
}
Response body (has 1 new remote item):
{
"conflictedItems": [],
"newRemoteItems": [
{
"id": 10,
"version": 1,
"name": "Ciryx",
"description": "Antique Cityx CPU",
"checked": false,
"quantity": 5,
"author": {
"id": 2,
"login": "[email protected]",
"name": "Miranda Johnson"
},
"device": {
"id": 3,
"name": "Apple iPhone"
},
}
]
}
Request body example (with fixed conflicted items):
{
"id": 1,
"name": "CPUs",
"description": "Processors for servers #1, #6, #11",
"author": {
"id": 1,
"login": "[email protected]",
"name": "Mark Johnson"
},
"device": {
"id": 1,
"name": "Samsung Galaxy S8"
},
"listItems": [
{
"id": 1,
"version": 0,
"name": "Core i5",
"description": "CPU Intel Core i5",
"checked": true,
"quantity": 2,
"force": true
},
{
"id": 2,
"version": 1,
"isDeleted": true,
"force": true
},
{
"id": 10,
"version": 1,
"name": "Ciryx",
"description": "Antique Cityx CPU",
"checked": false,
"quantity": 5,
"force": true
}
]
}
Response body: no conflicts.
In the current version the following functions are implemented particularly:
-
working with shopping list collaborators - supported authorship of editings, conflict resolving with providing info about editors, owners mangement; not supported storing collaborators in the database.
-
security - no integration with an external user log in system. Basic authorization with no user roles is used.