Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Filippo #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_store
.idea
node_modules
53 changes: 12 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,20 @@
# Kuama Backend Developer Test
## To get started:

Given the following [api specs](https://www.thecocktaildb.com/api.php)
Install node (v16.17.0 used).

Create a restful API that will show
- [ ] all drinks categories
- [ ] all drinks of type shot
- [ ] all drinks that give a results for the keyword `spritz`.
Run npm install, and then npm start.

For each drink, we want to know the following information:
- name
- glass
- instructions
- ingredients
- thumbnail
To test run npm test.

The keys of the json objects returned by your API should all be in camel case format
## Api endpoints:

Create the API using any language and framework you prefer.
Go to localhost:3000 and add endpoint:

If short on time, at least write down a list of todos with what you think is missing.
List all drink categories
/drinks/categories

## Bonus points
This is not a list to be followed top-bottom, just some enhancements you can do to show off 🙌 🚀
- [ ] Add a suite of tests
- [ ] Store results inside a persistence layer
- [ ] Setup a github action while making your PR to this repository

## How to deliver
If you know how: clone this repository, create a separate branch and make a PR

Otherwise: send us a zip with your project

## What will be rated?
- Code readability
- How you structure your project
- WTFs/minute

Remember: **it is not a problem if you don't complete your test**, as long as you keep our WTFs/minutes at a minimum rate.

And remember:

![programmer](https://raw.githubusercontent.com/Kuama-IT/kuama-frontend-test/master/programmer.png)


Have fun!

With ♥️ from Kuama folks
List all drinks of certain type: /drinks?type=shot
(or whatever type you want)

List all drinks by keyword: /drinks?name=spritz
(or whatever name you want)
91 changes: 91 additions & 0 deletions drink-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { HttpsHandler} from './https-handler.js';

const drinkFullEndpoint='lookup.php?i='; // + id drink
const categoriesEndpoint='list.php?c=list';
const filterCategoryEndpoint='filter.php?c='; // +category
const filterKeywordEndpoint='search.php?s='; // +keyword

export class DrinkController {

constructor() {
this.https = new HttpsHandler();
}

//convert api full drink info to requested ones
extractInfoFromFullDrink(fullDrink){
let drink={};
drink.name=fullDrink.strDrink;
drink.glass=fullDrink.strGlass;
drink.instructions=fullDrink.strInstructions;
drink.ingredients=[];
for (let i = 1; i <= 15; i++) { //api returns array of 15 ingredients
if (fullDrink['strIngredient'+i]!=null) drink.ingredients=fullDrink['strIngredient'+i];
}
drink.thumbnail=fullDrink.strDrinkThumb;
return drink;
}

getDrinkInfo(id){
return new Promise((resolve, reject) => {
this.https.getRequest(drinkFullEndpoint+id)
.then((data)=>{
resolve(data.drinks[0]);
})
.catch((error)=>{
reject(error);
})
});
}

getAllCategories(){
return new Promise((resolve, reject) => {
this.https.getRequest(categoriesEndpoint)
.then((data)=>{
const categories=[];
if (Array.isArray(data.drinks)){
data.drinks.forEach(element => {
categories.push(element.strCategory);
});
}
resolve(categories);
})
.catch((error)=>{
reject(error);
})
});
}

getDrinksByType(type){
return new Promise((resolve, reject) => {
this.https.getRequest(filterCategoryEndpoint+type)
.then((data)=>{
const idDrinks=[];
if (Array.isArray(data.drinks)){
data.drinks.forEach(element => {
idDrinks.push(element.idDrink);
});
}
Promise.allSettled(idDrinks.map((drink) => {
return this.getDrinkInfo(drink);
})).then((results)=>{
resolve(results.map((result)=>this.extractInfoFromFullDrink(result.value)));
});
})
.catch((error)=>{
reject(error);
})
});
}

getDrinksByKeyword(keyword){
return new Promise((resolve, reject) => {
this.https.getRequest(filterKeywordEndpoint+keyword)
.then((data)=>{
resolve(data.drinks.map((drink)=>this.extractInfoFromFullDrink(drink)));
})
.catch((error)=>{
reject(error);
})
});
}
}
30 changes: 30 additions & 0 deletions https-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as https from 'https';

const baseUrlApi='https://www.thecocktaildb.com/api/json/v1/1/';

export class HttpsHandler {

constructor() {
this.https = https;
}

getRequest(url){
return new Promise((resolve, reject) => {
this.https.get(baseUrlApi+url, res => {
let data = [];

res.on('data', chunk => {
data.push(chunk);
});

res.on('end', () => {
const parsedData = data.length==0 ? {} : JSON.parse(Buffer.concat(data).toString());
resolve(parsedData);
});
}).on('error', err => {
console.log('Error: ', err.message);
reject(err.message);
});
});
}
}
58 changes: 58 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import express from 'express';
import cors from 'cors';
import { DrinkController } from './drink-controller.js';

const app = express();
const port = 3000;
const drinkController = new DrinkController();

app.use(cors());

//list categories of drinks
app.get('/drinks/categories', (req, res) => {
drinkController.getAllCategories()
.then((categories)=>{
res.send(categories);
})
.catch((error)=>{
res.status(500).send(error);
})
});

//accepts only type and name query, if no one specified, send invalid requqest
app.get('/drinks', (req, res) => {
const filters = req.query;
if (filters.type)
drinkController.getDrinksByType(filters.type)
.then((drinks)=>{
res.send(drinks);
})
.catch((error)=>{
res.status(500).send(error);
})
else if (filters.name)
drinkController.getDrinksByKeyword(filters.name)
.then((drinks)=>{
res.send(drinks);
})
.catch((error)=>{
res.status(500).send(error);
})
else
res.status(400).send("Invalid request");
});

//optional, send the info of a single drink by id
app.get('/drink/:id', (req, res) => {
drinkController.getDrinkInfo(req.params.id)
.then((drink)=>{
res.send(drink);
})
.catch((error)=>{
res.status(500).send(error);
})
});

app.listen(port, () => console.log(`Server listening on port ${port}!`));

export default app;
Loading