-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy patherrors.py
158 lines (125 loc) · 5.35 KB
/
errors.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import json
from flask import current_app, jsonify, request
from jsonschema import ValidationError as JsonSchemaValidationError
from notifications_utils.eventlet import EventletTimeout
from notifications_utils.recipient_validation.errors import InvalidPhoneError, InvalidRecipientError
from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound
from app.authentication.auth import AuthError
from app.clients.document_download import DocumentDownloadError
from app.errors import InvalidRequest
class TooManyRequestsError(InvalidRequest):
status_code = 429
def __init__(self, limit_name, sending_limit):
self.limit_name = limit_name
self.sending_limit = sending_limit
self.message = f"Exceeded send limits ({limit_name}: {sending_limit}) for today"
class RateLimitError(InvalidRequest):
status_code = 429
message_template = "Exceeded rate limit for key type {} of {} requests per {} seconds"
def __init__(self, sending_limit, interval, key_type):
# normal keys are spoken of as "live" in the documentation
# so using this in the error messaging
if key_type == "normal":
key_type = "live"
self.message = self.message_template.format(key_type.upper(), sending_limit, interval)
class BadRequestError(InvalidRequest):
message = "An error occurred"
def __init__(self, fields=None, message=None, status_code=400):
self.status_code = status_code
self.fields = fields or []
self.message = message if message else self.message
class ValidationError(InvalidRequest):
message = "Your notification has failed validation"
def __init__(self, fields=None, message=None, status_code=400):
self.status_code = status_code
self.fields = fields or []
self.message = message if message else self.message
class PDFNotReadyError(BadRequestError):
def __init__(self):
super().__init__(message="PDF not available yet, try again later", status_code=400)
class QrCodeTooLongError(ValidationError):
message = "Cannot create a usable QR code - the link is too long"
def __init__(self, fields=None, message=None, status_code=400, *, num_bytes, max_bytes, data):
super().__init__(fields=fields, message=message, status_code=status_code)
self.num_bytes = num_bytes
self.max_bytes = max_bytes
self.data = data
def to_dict_v2(self):
"""
Version 2 of the public api error response.
"""
return {
"status_code": self.status_code,
"errors": [
{
"error": "ValidationError",
"message": self.message,
"data": self.data,
"num_bytes": self.num_bytes,
"max_bytes": self.max_bytes,
}
],
}
def register_errors(blueprint):
@blueprint.errorhandler(InvalidRecipientError)
def invalid_format(error):
current_app.logger.info(error)
return (
jsonify(
status_code=400,
errors=[{"error": error.__class__.__name__, "message": str(error)}],
),
400,
)
@blueprint.errorhandler(InvalidPhoneError)
def invalid_phone(error):
current_app.logger.info(error)
return (
jsonify(
status_code=400,
errors=[{"error": error.__class__.__name__, "message": error.get_legacy_v2_api_error_message()}],
),
400,
)
@blueprint.errorhandler(InvalidRequest)
def invalid_data(error):
current_app.logger.info(error)
response = jsonify(error.to_dict_v2()), error.status_code
return response
@blueprint.errorhandler(DocumentDownloadError)
def document_download_error(error: DocumentDownloadError):
current_app.logger.info(error)
# cast it to a BadRequestError to ensure we format the error well
bad_request_exc = BadRequestError(message=error.message)
response = jsonify(bad_request_exc.to_dict_v2()), error.status_code
return response
@blueprint.errorhandler(JsonSchemaValidationError)
def validation_error(error):
current_app.logger.info(error)
return jsonify(json.loads(error.message)), 400
@blueprint.errorhandler(NoResultFound)
@blueprint.errorhandler(DataError)
def no_result_found(e):
current_app.logger.info(e)
return jsonify(status_code=404, errors=[{"error": e.__class__.__name__, "message": "No result found"}]), 404
@blueprint.errorhandler(AuthError)
def auth_error(error):
current_app.logger.info("API AuthError, client: %s error: %s", request.headers.get("User-Agent"), error)
return jsonify(error.to_dict_v2()), error.code
@blueprint.errorhandler(EventletTimeout)
def eventlet_timeout(error):
current_app.logger.exception(error)
return (
jsonify(
status_code=504, errors=[{"error": error.__class__.__name__, "message": "Timeout serving request"}]
),
504,
)
@blueprint.errorhandler(Exception)
def internal_server_error(error):
current_app.logger.exception(error)
return (
jsonify(status_code=500, errors=[{"error": error.__class__.__name__, "message": "Internal server error"}]),
500,
)