fork download
  1. """Challenge 7: Validate an API Response
  2. Check an API response (status code + JSON body):
  3. - status equals the expected status
  4. - body is a JSON object containing every required field with the right type
  5. - fields with expected values match (a list/set/tuple means "any of these")
  6. Returns (is_valid, list_of_errors).
  7. """
  8.  
  9. import json
  10.  
  11.  
  12. def validate_api_response(response, required_fields, expected_values=None,
  13. expected_status=200):
  14. errors = []
  15.  
  16. if response.get("status") != expected_status:
  17. errors.append(f"unexpected status: {response.get('status')}")
  18.  
  19. # Body may arrive as a JSON string or an already-parsed dict
  20. body = response.get("body")
  21. if isinstance(body, str):
  22. try:
  23. body = json.loads(body)
  24. except json.JSONDecodeError:
  25. errors.append("body is not valid JSON")
  26. return False, errors
  27. if not isinstance(body, dict):
  28. errors.append("body missing or not a JSON object")
  29. return False, errors
  30.  
  31. # Required fields and their types
  32. for field, ftype in required_fields.items():
  33. if field not in body:
  34. errors.append(f"missing field: {field}")
  35. elif not isinstance(body[field], ftype):
  36. errors.append(f"wrong type for {field}: expected {ftype.__name__}")
  37.  
  38. # Expected values
  39. for field, expected in (expected_values or {}).items():
  40. if field not in body:
  41. continue # already reported if it was required
  42. allowed = expected if isinstance(expected, (list, set, tuple)) else [expected]
  43. if body[field] not in allowed:
  44. errors.append(f"invalid value for {field}: {body[field]!r}")
  45.  
  46. return (not errors), errors
  47.  
  48.  
  49.  
  50.  
  51. if __name__ == "__main__":
  52. required = {"id": int, "name": str, "state": str}
  53. expected = {"state": ["active", "pending"]}
  54.  
  55. good = {"status": 200, "body": {"id": 1, "name": "svc", "state": "active"}}
  56. bad_value = {"status": 200, "body": {"id": 1, "name": "svc", "state": "dead"}}
  57. missing = {"status": 200, "body": '{"id": 1}'} # JSON string body
  58. bad_status = {"status": 500, "body": {"id": 1, "name": "x", "state": "active"}}
  59. no_body = {"status": 200}
  60.  
  61. for label, resp in [("good", good), ("bad_value", bad_value), ("missing", missing),
  62. ("bad_status", bad_status), ("no_body", no_body)]:
  63. print(label, validate_api_response(resp, required, expected))
  64.  
  65. assert validate_api_response(good, required, expected)[0] is True
  66. assert validate_api_response(bad_value, required, expected)[0] is False
  67. assert len(validate_api_response(missing, required, expected)[1]) == 2
  68. assert validate_api_response(bad_status, required, expected)[0] is False
  69. assert validate_api_response(no_body, required, expected)[0] is False
  70. print("Challenge 7 OK")
  71.  
Success #stdin #stdout 0.16s 15688KB
stdin
Standard input is empty
stdout
good (True, [])
bad_value (False, ["invalid value for state: 'dead'"])
missing (False, ['missing field: name', 'missing field: state'])
bad_status (False, ['unexpected status: 500'])
no_body (False, ['body missing or not a JSON object'])
Challenge 7 OK