├── heroku_service ├── Procfile ├── config.ru ├── Gemfile ├── app.rb ├── app.json └── Gemfile.lock ├── webhooks.db ├── webhooks_skeleton.py ├── README.md ├── webhooks_skeleton.js ├── gendb.py ├── insert_rows.py ├── package.json └── .gitignore /heroku_service/Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec rackup config.ru -p $PORT 2 | -------------------------------------------------------------------------------- /webhooks.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/webhooks-project/master/webhooks.db -------------------------------------------------------------------------------- /heroku_service/config.ru: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | 4 | Bundler.require 5 | 6 | require './app.rb' 7 | run Sinatra::Application -------------------------------------------------------------------------------- /heroku_service/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'sinatra' 4 | 5 | group :development do 6 | gem 'foreman' 7 | gem 'heroku' 8 | end 9 | -------------------------------------------------------------------------------- /webhooks_skeleton.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | conn = sqlite3.connect('webhooks.db') 4 | c = conn.cursor() 5 | 6 | c.execute('SELECT * FROM transactions;') 7 | print c.fetchone() 8 | conn.close() 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nylas webhooks interview problem 2 | 3 | The goal of this interview is to build a very basic webhook system to POST JSON to https://nylas-webhooks-project.herokuapp.com/webhook. 4 | 5 | To do that, we'll need to extend the code in `webhooks_skeleton.py`. 6 | -------------------------------------------------------------------------------- /webhooks_skeleton.js: -------------------------------------------------------------------------------- 1 | const sqlite3 = require('sqlite3') 2 | 3 | let db = new sqlite3.Database('webhooks.db'); 4 | let sqlQuery = 'SELECT * FROM transactions'; 5 | 6 | db.all(sqlQuery, [], (err, rows) => { 7 | console.log(rows[0]); 8 | }); 9 | 10 | db.close(); 11 | -------------------------------------------------------------------------------- /gendb.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | conn = sqlite3.connect('webhooks.db') 4 | c = conn.cursor() 5 | c.execute("""CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, 6 | public_id VARCHAR(64), type VARCHAR(32), record_id INTEGER);""") 7 | 8 | conn.commit() 9 | conn.close() 10 | -------------------------------------------------------------------------------- /heroku_service/app.rb: -------------------------------------------------------------------------------- 1 | get '/' do 2 | "Nylas webhooks interview question 3 |

If you're seeing this, things have been installed correctly." 4 | end 5 | 6 | 7 | post '/webhook' do 8 | if rand(0..1) == 1 then 9 | [200, "Webhook processed!"] 10 | else 11 | [500, "Internal server error."] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /insert_rows.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | import random 3 | import sqlite3 4 | 5 | conn = sqlite3.connect('webhooks.db') 6 | c = conn.cursor() 7 | 8 | inserts = [] 9 | for i in range(100): 10 | public_id = str(uuid.uuid1()) 11 | type = random.choice(['create', 'update', 'delete']) 12 | record_id = random.randrange(1, 100000) 13 | inserts.append([public_id, type, record_id]) 14 | 15 | print inserts 16 | c.executemany('''INSERT INTO transactions (public_id, type, record_id) VALUES (?, ?, ?)''', inserts); 17 | 18 | conn.commit() 19 | conn.close() 20 | -------------------------------------------------------------------------------- /heroku_service/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sinatra-heroku-cedar-template", 3 | "description": "Sinatra Heroku Cedar Template - The bare minimum for a sinatra app on cedar, running thin, and using bundler.", 4 | "keywords": [ 5 | "sinatra", 6 | "heroku", 7 | "cedar", 8 | "template" 9 | ], 10 | "website": "https://github.com/scottmotte/sinatra-heroku-cedar-template", 11 | "repository": "https://github.com/scottmotte/sinatra-heroku-cedar-template", 12 | "logo": "https://raw.githubusercontent.com/scottmotte/sinatra-heroku-cedar-template/master/sinatra-heroku-cedar-template.jpg" 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webhooks-project", 3 | "version": "1.0.0", 4 | "description": "Nylas webhooks interview problem", 5 | "main": "webhooks_skeleton.js", 6 | "dependencies": { 7 | "sqlite3": "^4.0.2" 8 | }, 9 | "devDependencies": {}, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/nylas/webhooks-project.git" 16 | }, 17 | "author": "", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/nylas/webhooks-project/issues" 21 | }, 22 | "homepage": "https://github.com/nylas/webhooks-project#readme" 23 | } 24 | -------------------------------------------------------------------------------- /heroku_service/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.2.6) 5 | foreman (0.24.0) 6 | term-ansicolor (~> 1.0.5) 7 | thor (>= 0.13.6) 8 | heroku (2.17.0) 9 | launchy (>= 0.3.2) 10 | rest-client (~> 1.6.1) 11 | rubyzip 12 | term-ansicolor (~> 1.0.5) 13 | launchy (2.0.5) 14 | addressable (~> 2.2.6) 15 | mime-types (1.17.2) 16 | rack (1.4.0) 17 | rack-protection (1.2.0) 18 | rack 19 | rest-client (1.6.7) 20 | mime-types (>= 1.16) 21 | rubyzip (0.9.4) 22 | sinatra (1.3.2) 23 | rack (~> 1.3, >= 1.3.6) 24 | rack-protection (~> 1.2) 25 | tilt (~> 1.3, >= 1.3.3) 26 | term-ansicolor (1.0.7) 27 | thor (0.14.6) 28 | tilt (1.3.3) 29 | 30 | PLATFORMS 31 | ruby 32 | 33 | DEPENDENCIES 34 | foreman 35 | heroku 36 | sinatra 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | *.swp 104 | --------------------------------------------------------------------------------