diff --git a/Developer Evaluation.docx b/Developer Evaluation.docx new file mode 100644 index 0000000..1bf1171 Binary files /dev/null and b/Developer Evaluation.docx differ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3397ddc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:latest + +# Set working directoy +WORKDIR /usr/src/app + +# Copy dependencies and install +COPY package*.json ./ +RUN npm install + +# Copy code +COPY *.js ./ + +# Run the app +EXPOSE 3000 + +COPY start.sh . +RUN chmod 777 start.sh +CMD ./start.sh + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09450d8 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +test: app.js package.json + sudo docker build . -t paul/greymarket + sudo docker run -it -p 4916:3000 paul/greymarket diff --git a/README.md b/README.md index 0967ae8..3f09a59 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,76 @@ -# greymarketlabs-assignment +# Running the service +## Pre-Reqs +* Access to the interenet +* Docker environment installed with proper user permissions + +## Running +* This will use port 4916, if that is not available modify the Makefile +* Run `make` + +## Stopping +* Type `exit` + +## Testing +* Run `bash ./test.sh` in a seperate window on the same machine + +# API Description +## Setting Persona + +GET request to /set_persona with the following properties +* persona_id: integer (if not unique it will override a previous persona) +* first_name: string first name of persona +* last_name: string last name of persona +* interest_list: string comma seperated list of interests +* lat: floating point latitude of location +* lon: floating point longitude of location + +This will return a json formatted string of the values you set + +## Getting persona +GET request to /get_persona with the following propertie +* persona_id: integer id (error will occur if it does not exist) + +This will return a JSON in the following format +* id: integer persona id +* first_name: string first name +* last_name: string last name +* interest_list: comma seperated string of interests +* latitude: floating point latitude +* longitude: floating point longitude +* city: string city +* state: string state +* temperature: current temperature in fahrenheit + +# Further Questions +## A +I was not able to complete every aspect of this assignment. I few things I would have liked to accomplish in addition were: +* Proper error checking + * Currently it'll just fail out if bad inputs are given. I would have liked to have it error gracefully with proper error codes +* Clean up the code + * There are some odd variable names and no comments + * Several variables in the API itself change from input to output, I would have liked to make those the same +* Better id handling + * Have additional ways to determine what the next un-assigned ID would be + * Have a better way when getting the information that an ID doesn't exist +* Better testing + * Create a little more robust testing +* Temperature + * I grabbed the first temperature returned, rather than the current. It isn't too far off, but would have like to get the actual current temperature. + +## B +Potential issues include +* Poor error handling, the app will crash on bad input + * Provide more rubust intput checking with error code/string returns +* Interest list handling + * Right now it is just a comma seperated list + * I would have liked to have a seperate table of interests, then the returned json could have been an array list. This would have provided better access for the front end +* Repeat/Unknown IDs + * A repeat ID will overwrite perviously set values, this was a choice, but the API should have an update vs new option + * There could also be a way to not give an ID and the next ID is reaturned to the front end + * Unknown IDs on get are not handled and will cause a crash, there should be an error message returned + +## C +Possible security issues are: +* If anyone has arbetrary access they will be able to get all the persona information, this should be locked down to only authorized users +* Overloading the memory with large values, there is no limit to the length of the strings, thus someone could overwhelm the system by putting in too long of strings -Assignment for Grey Market Labs \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..209e077 --- /dev/null +++ b/app.js @@ -0,0 +1,140 @@ +const util = require('util'); +const express = require('express'); +const sqlite = require('sqlite3'); +const request = require('sync-request'); + +const app = express(); + +const persona_db = new sqlite.Database('./counter_persona.db', (err) => { + if(err) { + console.log(err.message); + exit(1); + } + + persona_db.exec(` + CREATE TABLE IF NOT EXISTS persona ( + id unsigned int primary key not null, + first_name text not null, + last_name text not null, + interest_list text not null, + latitude real not null, + longitude real not null + ); + `); +}); + +function on_err(err_str, err_code) { + console.log(err_str); + return { + "err": err_str, + "code": err_code, + }; +}; + +function insert_update_persona(user_params) { + persona_db.get("SELECT * FROM persona WHERE id = ?", [user_params.persona_id], (err, row) => { + if(row == undefined) { + persona_db.run(` + insert into persona + (id, first_name, last_name, interest_list, latitude, longitude) + values (?, ?, ?, ?, ?, ?) + `, [ + user_params.persona_id, + user_params.first_name, + user_params.last_name, + user_params.interest_list, + user_params.lat, + user_params.lon + ], (err) => { + if(err) { + console.log(err.message); + } + }); + } else { + persona_db.run(` + UPDATE persona SET + first_name = ?, + last_name = ?, + interest_list = ?, + latitude = ?, + longitude = ? + WHERE id = ? + `, [ + user_params.first_name, + user_params.last_name, + user_params.interest_list, + user_params.lat, + user_params.lon, + user_params.persona_id + ], (err) => { + if(err) { + console.log(err.message); + } + }); + } + }); +}; + +function get_weather(user_params) { + my_headers = {headers: {'user-agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36"}}; + weather_url = util.format("https://api.weather.gov/points/%d,%d", user_params.latitude, user_params.longitude); + + data = request('GET', weather_url, my_headers); + + data = JSON.parse(data.getBody()); + + user_params.city = data.properties.relativeLocation.properties.city; + user_params.state = data.properties.relativeLocation.properties.state; + + data = request('GET', data.properties.forecast, my_headers); + + data = JSON.parse(data.getBody()); + + user_params.temperature = data.properties.periods[0].temperature; + + return user_params; +}; + +app.get('/get_persona', function(req, res) { + // Gather params + user_params = { + persona_id: Number(req.query.persona_id), + }; + + persona_db.get("SELECT * FROM persona WHERE id = ?", [user_params.persona_id], (err, row) => { + if(err) { + console.error(err.message); + } + user_params = row; + user_params = get_weather(user_params); + console.log(user_params); + res.send(user_params); + }); +}); + +app.get('/set_persona', function(req, res) { + // Gather params + user_params = { + persona_id: parseInt(req.query.persona_id), + first_name: req.query.first_name, + last_name: req.query.last_name, + interest_list: req.query.interest_list, + lat: Number(req.query.lat), + lon: Number(req.query.lon), + }; + + // Verify params + for(const key in user_params) { + if(user_params[key] == undefined) { + res.send(on_err(util.format('%s must be defined', key), 1)); + return; + } + } + + insert_update_persona(user_params); + + res.send(user_params); +}); + +app.listen(3000, () => console.log('Server ready')); + diff --git a/package.json b/package.json new file mode 100644 index 0000000..665506d --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "greymarket", + "version": "1.0", + "description": "Grey Market assessment, tracking profiles", + "author": "Paul Halvorsen ", + "dependencies": { + "express": "", + "util": "", + "sqlite3": "", + "sync-request": "" + } +} diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..08af697 --- /dev/null +++ b/start.sh @@ -0,0 +1,3 @@ +#!/bin/bash +node app.js & +/bin/bash diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..8bceb2f --- /dev/null +++ b/test.sh @@ -0,0 +1,4 @@ +echo +curl "http://localhost:4916/set_persona?persona_id=1&first_name=hello&last_name=world&interest_list=a,b,c&lat=39.2673&lon=-76.7983" +curl "http://localhost:4916/get_persona?persona_id=1" +echo