| 4 | 4 | // |
| 5 | 5 | |
| 6 | 6 | export class RestAPI { |
| 7 | | constructor() { |
| 8 | | throw new Error('Remove this line and implement the function'); |
| 7 | constructor(database = { users: [] }) { |
| 8 | this.database = database; |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 11 | get(url) { |
| 12 | | throw new Error('Remove this line and implement the function'); |
| 12 | if (url.startsWith('/users')) { |
| 13 | let users = this.database.users; |
| 14 | |
| 15 | // Parse query parameters for filtering specific users |
| 16 | if (url.includes('?')) { |
| 17 | const queryString = url.split('?')[1]; |
| 18 | const params = new URLSearchParams(queryString); |
| 19 | const usersParam = params.get('users'); |
| 20 | |
| 21 | if (usersParam) { |
| 22 | // Parse the users parameter (could be comma-separated) |
| 23 | const requestedUsers = usersParam.split(',').map(name => name.trim()); |
| 24 | users = users.filter(user => requestedUsers.includes(user.name)); |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | // Sort users by name |
| 29 | const sortedUsers = users.sort((a, b) => a.name.localeCompare(b.name)); |
| 30 | return { users: sortedUsers }; |
| 31 | } |
| 32 | return null; |
| 13 | 33 | } |
| 14 | 34 | |
| 15 | 35 | post(url, payload) { |
| 16 | | throw new Error('Remove this line and implement the function'); |
| 36 | if (url === '/add') { |
| 37 | // Edge Case: Missing required fields in POST requests |
| 38 | if (!payload || !payload.user) { |
| 39 | return null; |
| 40 | } |
| 41 | |
| 42 | // Edge Case: Duplicate user creation |
| 43 | const existingUser = this.database.users.find(user => user.name === payload.user); |
| 44 | if (existingUser) { |
| 45 | return null; |
| 46 | } |
| 47 | |
| 48 | const newUser = { |
| 49 | name: payload.user, |
| 50 | owes: {}, |
| 51 | owed_by: {}, |
| 52 | balance: 0.0 |
| 53 | }; |
| 54 | |
| 55 | this.database.users.push(newUser); |
| 56 | return newUser; |
| 57 | } |
| 58 | |
| 59 | if (url === '/iou') { |
| 60 | // Edge Case: Missing required fields in POST requests |
| 61 | if (!payload || !payload.lender || !payload.borrower || payload.amount === undefined) { |
| 62 | return null; |
| 63 | } |
| 64 | |
| 65 | // Edge Case: Invalid user names in requests |
| 66 | const lender = this.database.users.find(user => user.name === payload.lender); |
| 67 | const borrower = this.database.users.find(user => user.name === payload.borrower); |
| 68 | |
| 69 | if (!lender || !borrower) { |
| 70 | return null; |
| 71 | } |
| 72 | |
| 73 | // Edge Case: Negative or zero amount values |
| 74 | if (payload.amount <= 0) { |
| 75 | return null; |
| 76 | } |
| 77 | |
| 78 | // Edge Case: Self-borrowing (user lending to themselves) |
| 79 | if (payload.lender === payload.borrower) { |
| 80 | return null; |
| 81 | } |
| 82 | |
| 83 | const amount = parseFloat(payload.amount); |
| 84 | |
| 85 | // Update lender's records |
| 86 | if (!lender.owed_by[payload.borrower]) { |
| 87 | lender.owed_by[payload.borrower] = 0; |
| 88 | } |
| 89 | lender.owed_by[payload.borrower] += amount; |
| 90 | |
| 91 | // Update borrower's records |
| 92 | if (!borrower.owes[payload.lender]) { |
| 93 | borrower.owes[payload.lender] = 0; |
| 94 | } |
| 95 | borrower.owes[payload.lender] += amount; |
| 96 | |
| 97 | // Recalculate balances |
| 98 | lender.balance = this.calculateBalance(lender); |
| 99 | borrower.balance = this.calculateBalance(borrower); |
| 100 | |
| 101 | // Handle debt offsetting |
| 102 | if (lender.owes[payload.borrower] && lender.owes[payload.borrower] > 0) { |
| 103 | const minAmount = Math.min(lender.owes[payload.borrower], lender.owed_by[payload.borrower]); |
| 104 | lender.owes[payload.borrower] -= minAmount; |
| 105 | lender.owed_by[payload.borrower] -= minAmount; |
| 106 | |
| 107 | if (lender.owes[payload.borrower] === 0) { |
| 108 | delete lender.owes[payload.borrower]; |
| 109 | } |
| 110 | if (lender.owed_by[payload.borrower] === 0) { |
| 111 | delete lender.owed_by[payload.borrower]; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | if (borrower.owed_by[payload.lender] && borrower.owed_by[payload.lender] > 0) { |
| 116 | const minAmount = Math.min(borrower.owes[payload.lender], borrower.owed_by[payload.lender]); |
| 117 | borrower.owes[payload.lender] -= minAmount; |
| 118 | borrower.owed_by[payload.lender] -= minAmount; |
| 119 | |
| 120 | if (borrower.owes[payload.lender] === 0) { |
| 121 | delete borrower.owes[payload.lender]; |
| 122 | } |
| 123 | if (borrower.owed_by[payload.lender] === 0) { |
| 124 | delete borrower.owed_by[payload.lender]; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | const result = [lender, borrower].sort((a, b) => a.name.localeCompare(b.name)); |
| 129 | return { users: result }; |
| 130 | } |
| 131 | |
| 132 | return null; |
| 133 | } |
| 134 | |
| 135 | calculateBalance(user) { |
| 136 | const totalOwed = Object.values(user.owes).reduce((sum, amount) => sum + amount, 0); |
| 137 | const totalOwedBy = Object.values(user.owed_by).reduce((sum, amount) => sum + amount, 0); |
| 138 | return parseFloat((totalOwedBy - totalOwed).toFixed(2)); |
| 17 | 139 | } |
| 18 | 140 | } |
| 141 | |
| 142 | // Handled Edge Cases: Duplicate user creation, Invalid user names in requests, Negative or zero amount values, Self-borrowing (user lending to themselves), Missing required fields in POST requests, Non-existent users in IOU requests, Empty user list in GET request |