This is a web application in which two foodstuffs duel each other :-)
Live demo @ koodihaaste.matiasraisanen.com
"techKeywords": [
"TypeScript",
"React",
"AWS CDK",
"Serverless",
"Github Actions",
"Jest",
"Fullstack",
"Linux"
]
The objective of this project is to take part in Solidabis code challenge 2022 // Solidabis koodihaaste 2022
Should the above URL be unavailable or updated, check this snapshot on the Wayback Machine from 11-Oct-2022
In the challenge the developer must create a fullstack application, in which a user can select two foodstuffs to "fight" each other.
The application must:
- Retrieve nutritional contents of foodstuffs from a 3rd party source
- Transform nutritional contents into character attributes
- Have logic that allows two foodstuffs to duel each other
- Present the duel results visually on the front end
I took the liberty of only using English in the project.
Fighter names (food items) will be Finnish though.
Here's a little list of some fighters to try out.
EN | FI |
---|---|
apple | omena |
carrot | porkkana |
tomato | tomaatti |
potato | peruna |
sausage | makkara |
You can run this application's frontend locally:
cd static && npm i && npm run start
Local tests can be run like so:
npm run test
The local version will still contact the actual production backend on AWS.
Backend can be deployed using CDK from under infra
folder, but in order for it to work you need to configure your own AWS credentials, which I will not go into here.
The application as a whole is hosted on Amazon Web Services
The back end is built and deployed using AWS Cloud Development Kit (AWS CDK), which lets the developer define the infrastructure as code.
The benefit here is that making changes to deployed infrastructure is easy, as it can be done through code and without the need to use any slow GUI.
CDK supports multiple programming languages, and I chose TypeScript as my language of choice. Its types bring some extra safety to writing complex web applications and lets me catch errors early.
The front end is a simple react app, created from scratch using create-react-app and React Bootstrap components.
It is hosted on AWS as a static website on an S3 Bucket.
The application has an automated CI/CD-pipeline using Github actions.
Every tagged release on Github will launch a deployment to AWS.
The pipeline can also be run manually, using a workflow dispatch.
The pipeline has also testing built in, so the application will not be deployed if a test fails.
Additionally to the automated CI/CD pipeline, the application can also be deployed from the local machine using the deploy script
bash ./deploy.sh
If you want to deploy the application, you have to change the deploy script to use your own AWS credentials, and also purchase a domain name to be used with the deployment.
-
On the front end, the user will type a food into a search box and press "submit".
-
The frontend will call API Gateway, which will forward the request to a lambda function
-
The lambda function will call Fineli foods API with the requested food.
-
Fineli responds with an array of all the items it found in its database.
-
The lambda function will then try to find the food item in its most unprocessed and raw form.
item.type.code = "FOOD" && item.preparationMethod[0].code = "RAW"
-
If no unprocessed version can be found, we will just fall back to the first item in the list.
-
The lambda function then converts the response into fighter stats.
{ "statusCode": 200, "message": "success", "data": { "originalName": "Omena, ulkomainen, kuorineen", "hp": 38.852571462547175, "damage": 8.19540006637573, "defense": 0.165299997925758, "wait": 8.447700065597887, "aps": 0.1183754148744419, "dps": 0.9701338829192556 } }
API response, fighter stats after conversion
-
Once the user has selected two fighters, they will press "FIGHT" to start the fight.
-
The frontend will then use simple maths to calculate the winner of the food fight, and display the fight results as a scrolling log.
Stat | Formula | Notes |
---|---|---|
Original Name | Name of the food item in Fineli's database | |
HP | energy (1kcal = 1hp) | Total health points |
DAMAGE | carbs (1g = 1pt) | Damage per strike |
DEFENSE (%) | protein (1g = 1%) | Mitigates damage from an incoming strike by a percentage. 1g = 1% |
WAIT | protein + carbs + fats = WAIT (sec) | Amount of seconds to wait after each strike. |
APS | 1 sec / WAIT | Number of attacks per second |
DPS | DAMAGE / WAIT | Damage per second. Not used for logic, but a nice to know stat |
After the user presses "fight", the fighters will keep firing attacks against each other at intervals defined by their wait time.
Damage from a single attack will be mitigated by the target's defense, after which the remaining damage points will be deducted from the target's HP.
Whoever runs out of HP first loses.
To avoid the audience getting bored of slow fights, the fights will be carried out at 100x speed :-)