- Context
- Objectives
- Approach
- Schema
- API System
- API Flows
- TDD-Red->Green->Refactor cycle
- Setup
- Libraries Used
- Challenges
- References
A Small, light-weight Rest API based Java application without any framework, which does not require any server.
Following are the primary objectives this project is based on
- The REST API should be built on an embedded server which means there should be no server or container that helps these API end points run at a particular application endpoint
- TDD should be enforced at every stage of the implementation
- The Application implementation should be simple and scalable as if it has real time traffic with multiple users accessing the system simultaneously
- The Application is meant to be Light weight hence Heavy frameworks such as Spring could not considered while implementing
- Application could only rely on in memory datastores
Based on the aforementioned objectives the system that is being built has been built based on the following approach:
- Using Jersey-Server library to create a JAX-RS reliant REST API server which can run as a self contained, embedded server that can be started and stopped from a simple Java application.
- Using Mockito and PowerMocktio frameworks the entire implementation is developed following the TDD strategy where fail tests -> refactor -> pass tests cycle was strictly carried on.
- JAX-RS application are simple, fast, reliable at the same time scaled based on the underlying data access implementations
- SQLite datastore is used to store the data without involving any sever runtime or containers to actively engage with data connections.
- Hibernate-JPA library is used to leverage the advantages javax.persistence library which helps in efficient, simple and scaled database access approaches.
Endpoint\HTTP METHOD | POST | GET |
---|---|---|
CRUD OP | CREATE | READ |
/users | Create new User | List existing users |
/users/1 | -- | Show Existing user |
/users/1/accounts | Create an account for user1 | List All Accounts of user 1 |
/users/1/accounts/1 | -- | Show Existing User's Chosen Account |
/users/1/accounts/1/transactions | Post a transaction to the given account - payload varies | Show Selected Account transactions |
/users/1/accounts/1/transactions/1 | -- | Show Chosen Transaction detail |
Following are the response status codes that are sent as part of the system
- 200 - OK
- 304 - Not Modified
- 400 - Bad Request
- 404 - Not Found
- 500 - Internal Server Error
Endpoint | Applicable status codes |
---|---|
/users | 200, 304, 500 |
/users/1 | 200, 404 |
/users/1/accounts | 200, 400, 500 |
/users/1/accounts/1 | 200, 400, 404 |
/users/1/accounts/1/transactions | 200, 400, 404, 500 |
/users/1/accounts/1/transactions/1 | 200, 304, 400, 404, 500, |
GET:
curl http://localhost:8080/users
HTTP STATUS: 200
[
{
"city": "Pasedena",
"dob": "11/03/1980",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
},
{
"city": "Pasedena",
"dob": "12/12/1983",
"email": "penny@cheesecakefactory.com",
"firstName": "Penny",
"gender": "female",
"id": 2,
"lastName": "Barrington",
"phoneNumber": "+8242312213123"
}
]
POST:
curl -d '{"city": "Pasedena","dob": "11/03/1980","email": "lenny@caltech.com","firstName": "Leonard","gender": "male","lastName": "Hofstader","phoneNumber": "+811297983423"}' -H "Content-Type: application/json" -X POST http://localhost:8080/users/
HTTP STATUS: 201
{
"city": "Pasedena",
"dob": "11/03/1980",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
Possible error messages:
- Record Creation Failed!
- Required Field(s) are Invalid! Field(s) :
- Unable to Find Record with given data!
- Internal Server Error! Please check Logs!
GET:
curl http://localhost:8080/users/1
HTTP STATUS: 200
{
"city": "Pasedena",
"dob": "11/03/1980",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
GET:
curl http://localhost:8080/users/1/accounts
HTTP STATUS: 200
[
{
"accountNumber": "2321342SFDS12",
"accountType": "SAVINGS",
"balance": 123.456,
"currency": "USD",
"id": 1,
"timeCreated": 1561723026458,
"user": {
"city": "Pasedena",
"dob": "12/12/2019",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
}
]
POST:
curl -d '{"accountNumber" : "2321342SFDS12","accountType" : "SAVINGS","currency" : "USD","balance" : 123.456}' -H "Content-Type: application/json" -X POST http://localhost:8080/users/1/accounts
HTTP STATUS: 201
{
"accountNumber": "2321342SFDS12",
"accountType": "SAVINGS",
"balance": 123.456,
"currency": "USD",
"id": 1,
"timeCreated": 1561723026458,
"user": {
"city": null,
"dob": null,
"email": null,
"firstName": null,
"gender": null,
"id": 1,
"lastName": null,
"phoneNumber": null
}
}
Possible error messages:
- Record Creation Failed!
- Required Field(s) are Invalid! Field(s) :
- Unable to Find Record with given data!
- Internal Server Error! Please check Logs!
GET:
curl http://localhost:8080/users/1/accounts/1
HTTP STATUS: 200
{
"accountNumber": "2321342SFDS12",
"accountType": "SAVINGS",
"balance": 123.456,
"currency": "USD",
"id": 1,
"timeCreated": 1561723026458,
"user": {
"city": "Pasedena",
"dob": "12/12/2019",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
}
GET:
curl http://localhost:8080/users/1/accounts
HTTP STATUS: 200
{
"message": "Record Creation Failed!"
}
POST (DEPOSIT):
curl -d '{"transactionType" : "CHEQUE_DEPOSIT","amount" : 100.00,"currency" : "USD"}' -H "Content-Type: application/json" -X POST http://localhost:8080/users/1/accounts/1
HTTP STATUS: 304
{
"amount": 100,
"currency": "USD",
"fromAccount": null,
"id": 1,
"timeCreated": 0,
"toAccount": {
"accountNumber": "2321342SFDS12",
"accountType": "SAVINGS",
"balance": 223.45600000000002,
"currency": "USD",
"id": 1,
"timeCreated": 1561723026458,
"user": {
"city": "Pasedena",
"dob": "12/12/2019",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
},
"transactionType": "CHEQUE_DEPOSIT"
}
POST (TRANSFER):
curl -d '{"transactionType" : "TRANSFFER", "fromAccountId" : 2,"amount" : 10.00,"currency" : "USD"}' -H "Content-Type: application/json" -X POST http://localhost:8080/users/1/accounts/1
HTTP STATUS: 304
{
"amount": 100,
"currency": "USD",
"fromAccount": {
"accountNumber": "1211342SFDS12",
"accountType": "SAVINGS",
"balance": 23.456000000000003,
"currency": "USD",
"id": 2,
"timeCreated": 1561731946466,
"user": {
"city": "Pasedena",
"dob": "12/12/2019",
"email": "penny@caltech.com",
"firstName": "Penny",
"gender": "female",
"id": 2,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
},
"id": 1,
"timeCreated": 1561732053415,
"toAccount": {
"accountNumber": "1211342SFDS12",
"accountType": "SAVINGS",
"balance": 223.45600000000002,
"currency": "USD",
"id": 1,
"timeCreated": 1561731933733,
"user": {
"city": "Pasedena",
"dob": "12/12/2019",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
},
"transactionType": "TRANSFER"
}
Possible transaction types:
- TRANSFER
- CHEQUE_DEPOSIT
- CASH_DEPOSIT
Possible error messages:
- Record Creation Failed!
- Unable to process transaction! Currency Conversion Not enabled
- Unable to process transaction! Insufficient Balance in you Account!!
- Required Field(s) are Invalid! Field(s) :
- Unable to Find Record with given data!
- Cannot transfer within the Same Account!
- Internal Server Error! Please check Logs!
GET:
curl http://localhost:8080/users/1/accounts/1/transactions/1
HTTP STATUS: 200
{
"amount": 100,
"currency": "USD",
"fromAccount": null,
"id": 1,
"timeCreated": 0,
"toAccount": {
"accountNumber": "2321342SFDS12",
"accountType": "SAVINGS",
"balance": 223.45600000000002,
"currency": "USD",
"id": 1,
"timeCreated": 1561723026458,
"user": {
"city": "Pasedena",
"dob": "12/12/2019",
"email": "lenny@caltech.com",
"firstName": "Leonard",
"gender": "male",
"id": 1,
"lastName": "Hofstader",
"phoneNumber": "+811297983423"
}
},
"transactionType": "CHEQUE_DEPOSIT"
}
All the internal errors including runtime errors are handled at controller and sent as a valid response to user following is an example
curl -d '{"accountNumber" : "2321342SFDS12","accountType" : "SAVINGS","currency" : "USD","balance" : 123.456}' -H "Content-Type: application/json" -X POST http://localhost:8080/users/1/accounts
HTTP STATUS: 304
{
"message": "Record Creation Failed!"
}
With Tests:
$ mvn clean install -U
Unit tests:
$ mvn test
Integration tests:
$ mvn integration-test
$ java com.revolut.assesment.project.server.EmbeddedJettyServer
Following are the Libraries that are used as part of source and test cycles
Developed in Jetbrain's IntelliJ IDE
- Had a lot of challenges in setting up environment first with maven as there was a certificate issue that was messing with my maven commands due to network provider issues
- Had difficulty in choosing the embedded server library as the prime objective was to keep the application light weight
- Initially started with H2 in-memory DB and found out the hard way that it cannot be alive without a server/container or a EJB environment like JBoss.
- Faced quite a lot of difficulties when trying to adapt the JAX-RS framework over the jetty-server library provided by sun.net and not the glassfish one.
- Chose SQLite as it was serving the purpose and lightweight but I had to write a lot of boilerplate code initially as I was not able to find any ORM support like JPA adapting to JAX-RS and embedded server format.
- But after spending a lot of time researching I have found a way to make JPA from hibernate support SQLite which let me throw away all my code involving explicit connection handling.
- Had issues trying to setup integration tests, tried glassfish-jersey-client and faced a lot of dependency mismatch issues but finally found the restassured.io that helped me adapt to the sun.net jersey-client library.
- Jersey-Server based Embedded Server supporting JAX-RS: https://dzone.com/articles/lightweight-embedded-java-rest-server-without-a-fr
- Enabling CORS for the Above embedded server approach: https://crunchify.com/what-is-cross-origin-resource-sharing-cors-how-to-add-it-to-your-java-jersey-web-server/
- https://dzone.com/articles/how-i-test-my-java-classes-for-thread-safety
- JAXRS-H2: https://github.com/dprasanthv/JAX-RS-JPA-Hibernate-H2-In-Memory-Database
- JPA-SQlite: https://github.com/juniorware/sqlite-jpa/