-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Instructions | ||
|
||
Implement a RESTful API for tracking IOUs. | ||
|
||
Four roommates have a habit of borrowing money from each other frequently, and have trouble remembering who owes whom, and how much. | ||
|
||
Your task is to implement a simple [RESTful API][restful-wikipedia] that receives [IOU][iou]s as POST requests, and can deliver specified summary information via GET requests. | ||
|
||
## API Specification | ||
|
||
### User object | ||
|
||
```json | ||
{ | ||
"name": "Adam", | ||
"owes": { | ||
"Bob": 12.0, | ||
"Chuck": 4.0, | ||
"Dan": 9.5 | ||
}, | ||
"owed_by": { | ||
"Bob": 6.5, | ||
"Dan": 2.75 | ||
}, | ||
"balance": "<(total owed by other users) - (total owed to other users)>" | ||
} | ||
``` | ||
|
||
### Methods | ||
|
||
| Description | HTTP Method | URL | Payload Format | Response w/o Payload | Response w/ Payload | | ||
| ------------------------ | ----------- | ------ | ------------------------------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------- | | ||
| List of user information | GET | /users | `{"users":["Adam","Bob"]}` | `{"users":<List of all User objects>}` | `{"users":<List of User objects for <users> (sorted by name)}` | | ||
| Create user | POST | /add | `{"user":<name of new user (unique)>}` | N/A | `<User object for new user>` | | ||
| Create IOU | POST | /iou | `{"lender":<name of lender>,"borrower":<name of borrower>,"amount":5.25}` | N/A | `{"users":<updated User objects for <lender> and <borrower> (sorted by name)>}` | | ||
|
||
## Other Resources | ||
|
||
- [REST API Tutorial][restfulapi] | ||
- Example RESTful APIs | ||
- [GitHub][github-rest] | ||
- [Reddit][reddit-rest] | ||
|
||
[restful-wikipedia]: https://en.wikipedia.org/wiki/Representational_state_transfer | ||
[iou]: https://en.wikipedia.org/wiki/IOU | ||
[github-rest]: https://developer.github.com/v3/ | ||
[reddit-rest]: https://web.archive.org/web/20231202231149/https://www.reddit.com/dev/api/ | ||
[restfulapi]: https://restfulapi.net/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"authors": [ | ||
"steffan153", | ||
"vaeng" | ||
], | ||
"files": { | ||
"solution": [ | ||
"rest-api.sql" | ||
], | ||
"test": [ | ||
"rest-api_test.sql" | ||
], | ||
"example": [ | ||
".meta/example.sql" | ||
] | ||
}, | ||
"blurb": "Implement a RESTful API for tracking IOUs." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
UPDATE 'rest-api' AS CURRENT | ||
SET result = ( | ||
SELECT json_object('users', IIF(json_type(DB.value) IS NULL, json_array(), json_array(DB.value))) | ||
FROM 'rest-api' AS RA | ||
LEFT JOIN json_each(RA.payload, '$.users') AS PL ON RA.payload = PL.json | ||
LEFT JOIN json_each(RA.database, '$.users') AS DB ON RA.database = DB.json AND json_extract(DB.value, '$.name') = PL.value | ||
WHERE (RA.database, RA.payload) = (CURRENT.database, CURRENT.payload) | ||
) | ||
WHERE url = '/users'; | ||
|
||
|
||
UPDATE 'rest-api' AS CURRENT | ||
SET result = json_object('name', json_extract(payload, '$.user'), 'owes', json_object(), 'owed_by', json_object(), 'balance', 0) | ||
WHERE url = '/add'; | ||
|
||
|
||
UPDATE 'rest-api' AS CURRENT | ||
SET result = json_array( | ||
-- update the lender: | ||
( | ||
SELECT json_object( | ||
'name', json_extract(payload, '$.lender'), | ||
'owes', json_patch( | ||
json_extract(value, '$.owes'), | ||
json_object( | ||
json_extract(payload, '$.borrower'), | ||
IIF(relative_balance < 0, -relative_balance, NULL) | ||
) | ||
), | ||
'owed_by', json_patch( | ||
json_extract(value, '$.owed_by'), | ||
json_object( | ||
json_extract(payload, '$.borrower'), | ||
IIF(relative_balance > 0, relative_balance, NULL) | ||
) | ||
), | ||
'balance', json_extract(value, '$.balance') + json_extract(payload, '$.amount') | ||
) | ||
FROM ( | ||
SELECT | ||
*, | ||
IFNULL(json_extract(json_extract(value, '$.owed_by'), '$.' || json_extract(payload, '$.borrower')), 0) - | ||
IFNULL(json_extract(json_extract(value, '$.owes'), '$.' || json_extract(payload, '$.borrower')), 0) + | ||
json_extract(payload, '$.amount') AS relative_balance | ||
FROM json_each(database, '$.users') | ||
WHERE json = database AND json_extract(value, '$.name') == json_extract(payload, '$.lender') | ||
) | ||
), | ||
-- update the borrower: | ||
( | ||
SELECT json_object( | ||
'name', json_extract(payload, '$.borrower'), | ||
'owes', json_patch( | ||
json_extract(value, '$.owes'), | ||
json_object( | ||
json_extract(payload, '$.lender'), | ||
IIF(relative_balance < 0, -relative_balance, NULL) | ||
) | ||
), | ||
'owed_by', json_patch( | ||
json_extract(value, '$.owed_by'), | ||
json_object( | ||
json_extract(payload, '$.lender'), | ||
IIF(relative_balance > 0, relative_balance, NULL) | ||
) | ||
), | ||
'balance', json_extract(value, '$.balance') - json_extract(payload, '$.amount') | ||
) | ||
FROM ( | ||
SELECT | ||
*, | ||
IFNULL(json_extract(json_extract(value, '$.owed_by'), '$.' || json_extract(payload, '$.lender')), 0) - | ||
IFNULL(json_extract(json_extract(value, '$.owes'), '$.' || json_extract(payload, '$.lender')), 0) - | ||
json_extract(payload, '$.amount') AS relative_balance | ||
FROM json_each(database, '$.users') | ||
WHERE json = database AND json_extract(value, '$.name') == json_extract(payload, '$.borrower') | ||
) | ||
) | ||
) | ||
WHERE url = '/iou'; | ||
|
||
-- order the result | ||
UPDATE 'rest-api' AS CURRENT | ||
SET result = ( | ||
SELECT json_object('users', json_group_array(json(value))) | ||
FROM ( | ||
SELECT value | ||
FROM json_each(result) | ||
ORDER BY value | ||
) | ||
) | ||
WHERE url = '/iou'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# This is an auto-generated file. | ||
# | ||
# Regenerating this file via `configlet sync` will: | ||
# - Recreate every `description` key/value pair | ||
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications | ||
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) | ||
# - Preserve any other key/value pair | ||
# | ||
# As user-added comments (using the # character) will be removed when this file | ||
# is regenerated, comments can be added via a `comment` key. | ||
|
||
[5be01ffb-a814-47a8-a19f-490a5622ba07] | ||
description = "user management -> no users" | ||
|
||
[382b70cc-9f6c-486d-9bee-fda2df81c803] | ||
description = "user management -> add user" | ||
|
||
[d624e5e5-1abb-4f18-95b3-45d55c818dc3] | ||
description = "user management -> get single user" | ||
|
||
[7a81b82c-7276-433e-8fce-29ce983a7c56] | ||
description = "iou -> both users have 0 balance" | ||
|
||
[1c61f957-cf8c-48ba-9e77-b221ab068803] | ||
description = "iou -> borrower has negative balance" | ||
|
||
[8a8567b3-c097-468a-9541-6bb17d5afc85] | ||
description = "iou -> lender has negative balance" | ||
|
||
[29fb7c12-7099-4a85-a7c4-9c290d2dc01a] | ||
description = "iou -> lender owes borrower" | ||
|
||
[ce969e70-163c-4135-a4a6-2c3a5da286f5] | ||
description = "iou -> lender owes borrower less than new loan" | ||
|
||
[7f4aafd9-ae9b-4e15-a406-87a87bdf47a4] | ||
description = "iou -> lender owes borrower same as new loan" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
DROP TABLE IF EXISTS "rest-api"; | ||
CREATE TABLE "rest-api" ( | ||
"database" TEXT, | ||
"payload" TEXT, | ||
"url" TEXT, | ||
"result" TEXT | ||
); | ||
|
||
.mode csv | ||
.import ./data.csv "rest-api" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
DROP TABLE IF EXISTS tests; | ||
CREATE TABLE IF NOT EXISTS tests ( | ||
-- uuid and name are taken from the test.toml file | ||
uuid TEXT PRIMARY KEY, | ||
name TEXT NOT NULL, | ||
-- The following section is needed by the online test-runner | ||
status TEXT DEFAULT 'fail', | ||
message TEXT, | ||
output TEXT, | ||
test_code TEXT, | ||
task_id INTEGER DEFAULT NULL, | ||
-- Here are columns for the actual tests | ||
database TEXT NOT NULL, | ||
payload TEXT NOT NULL, | ||
url TEXT INT NOT NULL, | ||
expected TEXT NOT NULL | ||
); | ||
|
||
-- Note: the strings below _may_ contain literal tab, newline, or carriage returns. | ||
|
||
INSERT INTO tests (uuid, name, database, payload, url, expected) | ||
VALUES | ||
("5be01ffb-a814-47a8-a19f-490a5622ba07", | ||
"no users", | ||
"{""users"":[]}", | ||
"{}", | ||
"/users", | ||
"{""users"":[]}"), | ||
|
||
("382b70cc-9f6c-486d-9bee-fda2df81c803", | ||
"add user", | ||
"{""users"":[]}","{""user"":""Adam""}", | ||
"/add", | ||
"{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0}"), | ||
|
||
("d624e5e5-1abb-4f18-95b3-45d55c818dc3", | ||
"get single user", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{},""owed_by"":{},""balance"":0}]}", | ||
"{""users"":[""Bob""]}", | ||
"/users", | ||
"{""users"":[{""name"":""Bob"",""owes"":{},""owed_by"":{},""balance"":0}]}"), | ||
|
||
("7a81b82c-7276-433e-8fce-29ce983a7c56", | ||
"both users have 0 balance", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{},""owed_by"":{},""balance"":0}]}", | ||
"{""lender"":""Adam"",""borrower"":""Bob"",""amount"":3}", | ||
"/iou", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{""Bob"":3},""balance"":3},{""name"":""Bob"",""owes"":{""Adam"":3},""owed_by"":{},""balance"":-3}]}"), | ||
|
||
("1c61f957-cf8c-48ba-9e77-b221ab068803", | ||
"borrower has negative balance", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{""Chuck"":3},""owed_by"":{},""balance"":-3},{""name"":""Chuck"",""owes"":{},""owed_by"":{""Bob"":3},""balance"":3}]}", | ||
"{""lender"":""Adam"",""borrower"":""Bob"",""amount"":3}", | ||
"/iou", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{""Bob"":3},""balance"":3},{""name"":""Bob"",""owes"":{""Adam"":3,""Chuck"":3},""owed_by"":{},""balance"":-6}]}" | ||
), | ||
|
||
("8a8567b3-c097-468a-9541-6bb17d5afc85", | ||
"lender has negative balance", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{""Chuck"":3},""owed_by"":{},""balance"":-3},{""name"":""Chuck"",""owes"":{},""owed_by"":{""Bob"":3},""balance"":3}]}", | ||
"{""lender"":""Bob"",""borrower"":""Adam"",""amount"":3}", | ||
"/iou", | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{""Chuck"":3},""owed_by"":{""Adam"":3},""balance"":0}]}"), | ||
|
||
("29fb7c12-7099-4a85-a7c4-9c290d2dc01a", | ||
"lender owes borrower", | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":3},""balance"":3}]}", | ||
"{""lender"":""Adam"",""borrower"":""Bob"",""amount"":2}", | ||
"/iou", | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":1},""owed_by"":{},""balance"":-1},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":1},""balance"":1}]}"), | ||
|
||
("ce969e70-163c-4135-a4a6-2c3a5da286f5", | ||
"lender owes borrower less than new loan", | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":3},""balance"":3}]}", | ||
"{""lender"":""Adam"",""borrower"":""Bob"",""amount"":4}", | ||
"/iou", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{""Bob"":1},""balance"":1},{""name"":""Bob"",""owes"":{""Adam"":1},""owed_by"":{},""balance"":-1}]}"), | ||
|
||
("7f4aafd9-ae9b-4e15-a406-87a87bdf47a4", | ||
"lender owes borrower same as new loan", | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":3},""balance"":3}]}", | ||
"{""lender"":""Adam"",""borrower"":""Bob"",""amount"":3}", | ||
"/iou", | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{},""owed_by"":{},""balance"":0}]}"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
"{""users"":[]}","{}","/users","" | ||
"{""users"":[]}","{""user"":""Adam""}","/add","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{},""owed_by"":{},""balance"":0}]}","{""users"":[""Bob""]}","/users","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{},""owed_by"":{},""balance"":0}]}","{""lender"":""Adam"",""borrower"":""Bob"",""amount"":3}","/iou","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{""Chuck"":3},""owed_by"":{},""balance"":-3},{""name"":""Chuck"",""owes"":{},""owed_by"":{""Bob"":3},""balance"":3}]}","{""lender"":""Adam"",""borrower"":""Bob"",""amount"":3}","/iou","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{},""owed_by"":{},""balance"":0},{""name"":""Bob"",""owes"":{""Chuck"":3},""owed_by"":{},""balance"":-3},{""name"":""Chuck"",""owes"":{},""owed_by"":{""Bob"":3},""balance"":3}]}","{""lender"":""Bob"",""borrower"":""Adam"",""amount"":3}","/iou","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":3},""balance"":3}]}","{""lender"":""Adam"",""borrower"":""Bob"",""amount"":2}","/iou","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":3},""balance"":3}]}","{""lender"":""Adam"",""borrower"":""Bob"",""amount"":4}","/iou","" | ||
"{""users"":[{""name"":""Adam"",""owes"":{""Bob"":3},""owed_by"":{},""balance"":-3},{""name"":""Bob"",""owes"":{},""owed_by"":{""Adam"":3},""balance"":3}]}","{""lender"":""Adam"",""borrower"":""Bob"",""amount"":3}","/iou","" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
-- Schema: CREATE TABLE "rest-api" ("database" TEXT, "payload" TEXT, "url" TEXT, "result" TEXT); | ||
-- Task: update the rest-api table and set the result based on the database, payload and url fields. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
-- Create database: | ||
.read ./create_fixture.sql | ||
|
||
-- Read user student solution and save any output as markdown in user_output.md: | ||
.mode markdown | ||
.output user_output.md | ||
.read ./rest-api.sql | ||
.output | ||
|
||
-- Create a clean testing environment: | ||
.read ./create_test_table.sql | ||
|
||
-- Comparison of user input and the tests updates the status for each test: | ||
UPDATE tests | ||
SET status = 'pass' | ||
FROM (SELECT database, url, payload, result FROM 'rest-api') AS actual | ||
WHERE (actual.database, actual.url, actual.payload) = (tests.database, tests.url, tests.payload) | ||
AND (SELECT COUNT(*) FROM json_tree(actual.result) as a, json_tree(tests.expected) as b WHERE (a.fullkey, a.atom) IS (b.fullkey, b.atom)) = | ||
(SELECT COUNT(*) FROM json_tree(tests.expected)) | ||
AND (SELECT COUNT(*) FROM json_tree(tests.expected)) = (SELECT COUNT(*) FROM json_tree(actual.result)); | ||
|
||
-- Update message for failed tests to give helpful information: | ||
UPDATE tests | ||
SET message = 'Result for ' || tests.database || ' as ' || tests.payload || ' is ' || actual.result || ', but should be ' || tests.expected | ||
FROM (SELECT database, payload, url, result FROM 'rest-api') AS actual | ||
WHERE (actual.database, actual.payload, actual.url) = (tests.database, tests.payload, tests.url) AND tests.status = 'fail'; | ||
|
||
-- Save results to ./output.json (needed by the online test-runner) | ||
.mode json | ||
.once './output.json' | ||
SELECT name, status, message, output, test_code, task_id | ||
FROM tests; | ||
|
||
-- Display test results in readable form for the student: | ||
.mode table | ||
SELECT name, status, message | ||
FROM tests; |