|
| 1 | +# Assignment |
| 2 | + |
| 3 | +You'll set up and work with your own version of a simple Contacts API. |
| 4 | + |
| 5 | +It will start with one endpoint (and you will add more throughout the task): |
| 6 | + |
| 7 | +- `GET /api/contacts` |
| 8 | + |
| 9 | +This endpoint accepts a query parameter `sort`. Here's how it should be possible to use it: |
| 10 | + |
| 11 | +- `GET /api/contacts?sort=first_name%20ASC` |
| 12 | + - Sorts contacts by first name, ascending |
| 13 | +- `GET /api/contacts?sort=last_name%20DESC` |
| 14 | + - Sorts contacts by last name, descending |
| 15 | + |
| 16 | +## Setup |
| 17 | + |
| 18 | +1. Go to/create a `node/week2` directory in your `hyf-assignment` repo. |
| 19 | +2. Create yourself a new node application |
| 20 | +3. Create a database called `phonebook` with a `contacts` table, with the following schema and data: |
| 21 | + |
| 22 | +```sql |
| 23 | +CREATE TABLE `contacts` ( |
| 24 | + `id` int unsigned NOT NULL AUTO_INCREMENT, |
| 25 | + `first_name` varchar(255) NOT NULL, |
| 26 | + `last_name` varchar(255) NOT NULL, |
| 27 | + `email` varchar(255) DEFAULT NULL, |
| 28 | + `phone` varchar(255) DEFAULT NULL, |
| 29 | + PRIMARY KEY (`id`) |
| 30 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
| 31 | + |
| 32 | +-- Sample data |
| 33 | +insert into contacts (id, first_name, last_name, email, phone) values ( 1, 'Selig', 'Matussov', '[email protected]', '176-630-4577'); |
| 34 | +insert into contacts (id, first_name, last_name, email, phone) values (2, 'Kenny', 'Yerrington', null, null); |
| 35 | +insert into contacts (id, first_name, last_name, email, phone) values (3, 'Emilie', 'Gaitskell', null, null); |
| 36 | +insert into contacts (id, first_name, last_name, email, phone) values (4, 'Jordon', 'Tokell', null, null); |
| 37 | +insert into contacts (id, first_name, last_name, email, phone) values ( 5, 'Sallyann', 'Persse', '[email protected]', '219-157-2368'); |
| 38 | +insert into contacts (id, first_name, last_name, email, phone) values (6, 'Berri', 'Bulter', null, null); |
| 39 | +insert into contacts (id, first_name, last_name, email, phone) values ( 7, 'Lanni', 'Ivanilov', '[email protected]', null); |
| 40 | +insert into contacts (id, first_name, last_name, email, phone) values (8, 'Dagny', 'Milnthorpe', null, null); |
| 41 | +insert into contacts (id, first_name, last_name, email, phone) values (9, 'Annadiane', 'Bansal', null, null); |
| 42 | +insert into contacts (id, first_name, last_name, email, phone) values (10, 'Tawsha', 'Hackley', null, null); |
| 43 | +insert into contacts (id, first_name, last_name, email, phone) values (11, 'Rubetta', 'Ozelton', null, null); |
| 44 | +insert into contacts (id, first_name, last_name, email, phone) values ( 12, 'Charles', 'Boughey', '[email protected]', '605-358-5664'); |
| 45 | +insert into contacts (id, first_name, last_name, email, phone) values (13, 'Shantee', 'Robbe', null, null); |
| 46 | +insert into contacts (id, first_name, last_name, email, phone) values (14, 'Gleda', 'Peat', null, null); |
| 47 | +insert into contacts (id, first_name, last_name, email, phone) values ( 15, 'Arlinda', 'Ethersey', '[email protected]', '916-139-1300'); |
| 48 | +insert into contacts (id, first_name, last_name, email, phone) values ( 16, 'Armando', 'Meachem', '[email protected]', '631-442-5339'); |
| 49 | +insert into contacts (id, first_name, last_name, email, phone) values (17, 'Codi', 'Redhouse', null, '401-953-6897'); |
| 50 | +insert into contacts (id, first_name, last_name, email, phone) values ( 18, 'Ann', 'Buncombe', '[email protected]', '210-338-0748'); |
| 51 | +insert into contacts (id, first_name, last_name, email, phone) values ( 19, 'Louis', 'Matzkaitis', '[email protected]', '583-996-6979'); |
| 52 | +insert into contacts (id, first_name, last_name, email, phone) values (20, 'Jessey', 'Pala', null, null); |
| 53 | +insert into contacts (id, first_name, last_name, email, phone) values ( 21, 'Archy', 'Scipsey', '[email protected]', '420-983-2426'); |
| 54 | +insert into contacts (id, first_name, last_name, email, phone) values ( 22, 'Benoit', 'Mould', '[email protected]', '271-217-9218'); |
| 55 | +insert into contacts (id, first_name, last_name, email, phone) values ( 23, 'Sherm', 'Girardey', '[email protected]', '916-999-2957'); |
| 56 | +insert into contacts (id, first_name, last_name, email, phone) values ( 24, 'Raquel', 'Mudge', '[email protected]', '789-830-7473'); |
| 57 | +insert into contacts (id, first_name, last_name, email, phone) values (25, 'Tabor', 'Reavey', null, null); |
| 58 | +``` |
| 59 | + |
| 60 | +4. Set up Express and an Sqlite connection in your node application. In your knex instance, make sure to set: `multipleStatements: true` - this is important! |
| 61 | + |
| 62 | +5. Make sure you have an API router under the `/api` path set up like so: |
| 63 | + |
| 64 | +```js |
| 65 | +app.use("/api", apiRouter); |
| 66 | +``` |
| 67 | + |
| 68 | +6. Create a contacts router at `/contacts`, and attach it to your API router. |
| 69 | +7. In your contacts API, create the following endpoint: |
| 70 | + |
| 71 | +```js |
| 72 | +contactsAPIRouter.get("/", async (req, res) => { |
| 73 | + let query = knexInstance.select("*").from("contacts"); |
| 74 | + |
| 75 | + if ("sort" in req.query) { |
| 76 | + const orderBy = req.query.sort.toString(); |
| 77 | + if (orderBy.length > 0) { |
| 78 | + query = query.orderByRaw(orderBy); |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + console.log("SQL", query.toSQL().sql); |
| 83 | + |
| 84 | + try { |
| 85 | + const data = await query; |
| 86 | + res.json({ data }); |
| 87 | + } catch (e) { |
| 88 | + console.error(e); |
| 89 | + res.status(500).json({ error: "Internal server error" }); |
| 90 | + } |
| 91 | +}); |
| 92 | +``` |
| 93 | + |
| 94 | +## The tasks |
| 95 | + |
| 96 | +### Task 1 - Solve the SQL injection |
| 97 | + |
| 98 | +The current implementation of the `sort` query parameter has introduced an SQL injection vulnerability. |
| 99 | + |
| 100 | +First, you should demonstrate the SQL injection and that, for instance, it is possible to drop/delete the `contacts` table with the `sort` query parameter. Capture this demonstration with a screen recording, and attach it to your PR when you submit your assignment. |
| 101 | + |
| 102 | +After having demonstrated the SQL injection vulnerability, your task is then to fix the issue. Your solution should be solved in the `app.js` file only. While the the `multipleStatements: true` configuration you used enables this vulnerability, it should not be changed in your solution. |
| 103 | + |
| 104 | +### Task 2 - Improve your API |
| 105 | + |
| 106 | +Create two additional endpoints to enable the following functionality: |
| 107 | + |
| 108 | +1. Create new contacts |
| 109 | +2. Delete an existing contact |
| 110 | + |
| 111 | +### Task 3 - Error handling |
| 112 | + |
| 113 | +Update your endpoints with appropriate error handling. You should, at least, handle the following cases: |
| 114 | + |
| 115 | +1. Successful requests |
| 116 | +2. Incorrect requests (e.g. an incorrectly formatted sort request) |
| 117 | +3. Server issues (e.g. a missing database table, or an offline database) |
| 118 | +4. A catch all for any other errors |
| 119 | + |
| 120 | +Remember to: |
| 121 | + |
| 122 | +1. Return the appropriate HTTP code |
| 123 | +2. Avoid sending any implementation or internal data to the client |
| 124 | +3. Log an appropriate message so you can debug issues that occur in your service |
| 125 | + |
| 126 | +### Task 3 - Postman |
| 127 | + |
| 128 | +1. Create a Postman collection to capture some example requests with your new API. |
| 129 | +2. Create a basic test suite that you can run to validate that everything is working correctly. |
| 130 | + |
| 131 | +Share both a link to your Collection and a link to a test run showing your tests passing in your pull request. |
0 commit comments