Capital Calls Web App
The Web App is made using a Python API server and React client. Bootstrap is primarily used for within React components and the grid/table view is made using React Data Grid from Adazzle.
The API server is built using Flask using Python 3.7. A REST API is developed with the help of Flask-RESTful.
The server access a SQLite3 database when running on Windows Hosts whereas a PostgreSQL when running in a Docker Container. The API queries the database thorugh Object Relational Mapping (ORM) using SQLAlchemy.
The API development follow a Test Driven Development (TDD) approach. Test are written using unittest
module avaialble within Python. This approach helps in design first and code later approach. Key specifications for the API were determined and then individual test were written. The actual code were then written for the test to pass.
1. Running
The API server can run either on a Windows Host having Python 3.7 or higher or on a Docker container using Docker Compose file.
In either case the API server depends on some environment variables that need to be set prior to running the server, these are explained further.
1.1. On Windows Host
1.1.1. Run the server
Python 3.7 should be installed on the Windows host and added to PATH environment variable. This can be checked in command as follows:
$> python -V
# Python 3.7.3
$> pip -V
# pip 19.1.1 from C:\Users\<UserName>\AppData\Local\Continuum\anaconda3\lib\site-packages\pip (python 3.7)
To run the server, navigate to the ./services/server
folder and run main.py
as shown below. This will run the server on 'http://localhost:5000'. Test by opening it and trying /funds, /committments, /capitalcalls or /investments on the browser.
$> cd services/server
$ (services/server)> python -m venv env
$ (services/server)> env/Scripts/activate
$(env) (services/server)> pip install -r requirements
$(env) (services/server)> python main.py run
To run tests or code coverage provide test
or cov
as argument to main.py
instead of run.
$> (services/server)> python main.py test
$> (services/server)> python main.py cov
1.1.2. Run the client
NodeJS v10.16 and NPM v6.9 should be installed on the host machine. This can be checked with following commands.
$> node --version
# v10.16.3
$> npm --version
# 6.9.0
To run the ReactJS client navigate to the ./services/client
folder and run npm run local
to run on localhost. This will start the development server and will set environment vaiable for server i.e. to localhost, assuming API server is runnning as per 5.1.1. above.
$> cd services/client
$ (services/client)> npm install
$ (services/client)> npm rum local
After this navigate to 'http://localhoat:3000' to access the ReactJS client.
Note that npm start
or npm run build
commands will not get access to API server for localhost.
1.2. Docker
Dockerfiles and Docker compose files are provided in services/server
and parent folder. Docker compose is build up of three containers:
- Python Alpine to run the Flask API server
- Postres Alpine to run the PostgreSQL databse server
- Node Alpine to run the ReactJS client server
To run the web app in Docker container execute the following commands.
$> docker-compose up --build
The above will build the all the service containers provided in thhe docker-compose.yml
file. To run as daemon add -d
flag to above command.
Once the services are running navigate to http://192.168.99.100:3007 to access the react-client.
Important
Note, the Docker services will run depending on what version of Docker is available on host machine i.e. Legacy Docker Toolbox or the new Docker for Windows, etc. Depending on the Docker, REACT_APP_USERS_SERVICE_URL
environment variable needs to amended in the docker.
The address http://192.168.99.100 is what I obtsained from Legacy Docker Toolbox.
PORTS
It is important to note the ports where the web app runs specially in a Docker container. Ports list are given in table below
Run Environment | Client | Server |
---|---|---|
Windows Host | localhost:3000 | localhost:5000 |
Docker Container | localhost:3007 | localhost:5001 |
2. API Endpoints
Using the RESTfull best practice alongwith TDD, the following routes are available in the API.
Endpoint | HTTP Method | CRUD Method | Result |
---|---|---|---|
/funds | POST | Create | Add one fund |
/funds | GET | Read | Get all funds |
/funds/:id | GET | Read | Get one fund |
/funds/:id | PUT | Update | Update one fund |
/funds/:id | DELETE | Delete | Delete one fund |
/committments | POST | Create | Add one committment |
/committments | GET | Read | Get all committments |
/committments/:id | GET | Read | Get one committment |
/committments/:id | PUT | Update | Update one committment |
/committments/:id | DELETE | Delete | Delete one committment |
/capitalcalls | POST | Create | Add one capital call |
/capitalcalls | GET | Read | Get all capital calls |
/capitalcalls/:id | GET | Read | Get one capital call |
/capitalcalls/:id | PUT | Update | Update one capital call |
/capitalcalls/:id | DELETE | Delete | Delete one capital call |
/investments | POST | Create | Add one investment |
/investments | GET | Read | Get all investments |
/investments/:id | GET | Read | Get one investment |
/investments/:id | PUT | Update | Update one investment |
/investments/:id | DELETE | Delete | Delete one investment |
3. API Requests and Responses
API requests can be mad using Fetch API or more conveniently and easily using Axios. Some basic requests examples are shown below.
-
GET requests to /funds
Axios.get(`${SERVER_URL}/funds`) .then(res => res.json()) .then(data => res.data) .catch(err => console.log(err));
-
GET requests to /committment/:id
Axios.get(`${SERVER_URL}/committments/${id}`) .then(res => res.json()) .then(data => res.data) .catch(err => console.log(err));
Assuming the API server is running on localhost, the SERVER_URL = "http://localhost:5000".
The response from API requests follows a standardized output of key-value pair objects. Every response will have three keys - status
, message
and data
. The status
key will contain values either success
or fail
. The data
key will have values that are either object (recieved for POST, GET and PUT request) or list of objects (for GET request). The data
key will be empty object for DELETE request.
Standard response structure
{
status: 'success', // or 'fail'
message: 'API response message',
data: [{
'key': value
}]
}
Example Response in data
key
-
GET response from /funds
{ status: 'success', message: 'API response message', data: [ { 'id': (number), 'name: (string) },{ 'id': (number), 'name: (string) }, ... ] }
Known issues
- API Server does not take querry commands such as filtering within URL. As a result API calls could be expensive when large data is there.
- Flash messaging or form validation is not present for good user experience.
- Post querries specially for posting capital investment is not task-queue based, as a result when multiples users are using the app, the app FIFO logic could break.
- Manage states, routes and components more appropriately in the React application.
- Small numbe of API test fail due to code changes, this does not impact code functionality.
4. References
- Testdriven.io
- Daniel Gaspar and Jack Stouffer, Mastering Flask Development, PacktPublishing Ltd., Oct 2018.
- Docker