| 1 | import json |
| 2 | |
| 1 | 3 | class RestAPI: |
| 2 | 4 | def __init__(self, database: dict | None = None): |
| 3 | | pass |
| 4 | | |
| 5 | # Initialize the database with provided data or an empty structure |
| 6 | if database is None: |
| 7 | self.database = {"users": []} |
| 8 | else: |
| 9 | self.database = database |
| 10 | |
| 5 | 11 | def get(self, url: str, payload: str | None = None) -> str: |
| 6 | | pass |
| 7 | | |
| 12 | # Edge Case: Handle invalid URL |
| 13 | if url != "/users": |
| 14 | return json.dumps({"error": "Invalid URL"}) |
| 15 | |
| 16 | # Edge Case: Handle empty database |
| 17 | if not self.database["users"]: |
| 18 | return json.dumps({"users": []}) |
| 19 | |
| 20 | # Parse payload if provided |
| 21 | if payload: |
| 22 | try: |
| 23 | data = json.loads(payload) |
| 24 | # Edge Case: Handle missing 'users' key in payload |
| 25 | if "users" not in data: |
| 26 | return json.dumps({"error": "Invalid payload"}) |
| 27 | |
| 28 | # Filter users based on payload |
| 29 | requested_users = data["users"] |
| 30 | users = [user for user in self.database["users"] if user["name"] in requested_users] |
| 31 | # Sort users by name |
| 32 | users.sort(key=lambda x: x["name"]) |
| 33 | return json.dumps({"users": users}) |
| 34 | except json.JSONDecodeError: |
| 35 | # Edge Case: Handle invalid JSON in payload |
| 36 | return json.dumps({"error": "Invalid JSON"}) |
| 37 | else: |
| 38 | # Return all users sorted by name |
| 39 | users = sorted(self.database["users"], key=lambda x: x["name"]) |
| 40 | return json.dumps({"users": users}) |
| 41 | |
| 8 | 42 | def post(self, url: str, payload: str | None = None) -> str: |
| 9 | | pass |
| 43 | # Edge Case: Handle invalid URL |
| 44 | if url not in ["/add", "/iou"]: |
| 45 | return json.dumps({"error": "Invalid URL"}) |
| 46 | |
| 47 | # Edge Case: Handle missing payload |
| 48 | if not payload: |
| 49 | return json.dumps({"error": "Missing payload"}) |
| 50 | |
| 51 | try: |
| 52 | data = json.loads(payload) |
| 53 | except json.JSONDecodeError: |
| 54 | # Edge Case: Handle invalid JSON in payload |
| 55 | return json.dumps({"error": "Invalid JSON"}) |
| 56 | |
| 57 | if url == "/add": |
| 58 | # Edge Case: Handle missing 'user' key in payload |
| 59 | if "user" not in data: |
| 60 | return json.dumps({"error": "Missing user field"}) |
| 61 | |
| 62 | user_name = data["user"] |
| 63 | |
| 64 | # Edge Case: Handle duplicate user |
| 65 | for user in self.database["users"]: |
| 66 | if user["name"] == user_name: |
| 67 | return json.dumps({"error": "User already exists"}) |
| 68 | |
| 69 | # Create new user |
| 70 | new_user = { |
| 71 | "name": user_name, |
| 72 | "owes": {}, |
| 73 | "owed_by": {}, |
| 74 | "balance": 0.0 |
| 75 | } |
| 76 | self.database["users"].append(new_user) |
| 77 | return json.dumps(new_user) |
| 78 | |
| 79 | elif url == "/iou": |
| 80 | # Edge Case: Handle missing required fields in payload |
| 81 | required_fields = ["lender", "borrower", "amount"] |
| 82 | for field in required_fields: |
| 83 | if field not in data: |
| 84 | return json.dumps({"error": f"Missing {field} field"}) |
| 85 | |
| 86 | lender_name = data["lender"] |
| 87 | borrower_name = data["borrower"] |
| 88 | amount = data["amount"] |
| 89 | |
| 90 | # Edge Case: Handle invalid amount type |
| 91 | if not isinstance(amount, (int, float)): |
| 92 | return json.dumps({"error": "Invalid amount"}) |
| 93 | |
| 94 | # Edge Case: Handle negative amount |
| 95 | if amount < 0: |
| 96 | return json.dumps({"error": "Amount cannot be negative"}) |
| 97 | |
| 98 | # Edge Case: Handle lender and borrower being the same person |
| 99 | if lender_name == borrower_name: |
| 100 | return json.dumps({"error": "Lender and borrower cannot be the same"}) |
| 101 | |
| 102 | # Find lender and borrower in database |
| 103 | lender = None |
| 104 | borrower = None |
| 105 | for user in self.database["users"]: |
| 106 | if user["name"] == lender_name: |
| 107 | lender = user |
| 108 | elif user["name"] == borrower_name: |
| 109 | borrower = user |
| 110 | |
| 111 | # Edge Case: Handle lender not found |
| 112 | if lender is None: |
| 113 | return json.dumps({"error": "Lender not found"}) |
| 114 | |
| 115 | # Edge Case: Handle borrower not found |
| 116 | if borrower is None: |
| 117 | return json.dumps({"error": "Borrower not found"}) |
| 118 | |
| 119 | # Update IOUs |
| 120 | # Update lender's owed_by |
| 121 | if borrower_name in lender["owed_by"]: |
| 122 | lender["owed_by"][borrower_name] += amount |
| 123 | else: |
| 124 | lender["owed_by"][borrower_name] = amount |
| 125 | |
| 126 | # Update borrower's owes |
| 127 | if lender_name in borrower["owes"]: |
| 128 | borrower["owes"][lender_name] += amount |
| 129 | else: |
| 130 | borrower["owes"][lender_name] = amount |
| 131 | |
| 132 | # Recalculate balances |
| 133 | lender_total_owed = sum(lender["owed_by"].values()) |
| 134 | lender_total_owes = sum(lender["owes"].values()) |
| 135 | lender["balance"] = lender_total_owed - lender_total_owes |
| 136 | |
| 137 | borrower_total_owed = sum(borrower["owed_by"].values()) |
| 138 | borrower_total_owes = sum(borrower["owes"].values()) |
| 139 | borrower["balance"] = borrower_total_owed - borrower_total_owes |
| 140 | |
| 141 | # Return updated users sorted by name |
| 142 | updated_users = sorted([lender, borrower], key=lambda x: x["name"]) |
| 143 | return json.dumps({"users": updated_users}) |
| 144 | # Handled Edge Cases: Invalid URL, empty database, missing 'users' key in payload, invalid JSON in payload, missing payload, missing 'user' key in payload, duplicate user, missing required fields in payload, invalid amount type, negative amount, lender and borrower being the same person, lender not found, borrower not found |