qena-shared-lib 0.1.10__py3-none-any.whl → 0.1.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qena_shared_lib/application.py +16 -11
- qena_shared_lib/background.py +14 -11
- qena_shared_lib/dependencies/http.py +3 -3
- qena_shared_lib/exception_handlers.py +4 -0
- qena_shared_lib/exceptions.py +59 -49
- qena_shared_lib/http.py +57 -3
- qena_shared_lib/logging.py +3 -3
- qena_shared_lib/logstash/_base.py +31 -30
- qena_shared_lib/logstash/_http_sender.py +1 -1
- qena_shared_lib/logstash/_tcp_sender.py +9 -9
- qena_shared_lib/rabbitmq/_base.py +32 -26
- qena_shared_lib/rabbitmq/_channel.py +24 -14
- qena_shared_lib/rabbitmq/_exception_handlers.py +3 -3
- qena_shared_lib/rabbitmq/_listener.py +46 -47
- qena_shared_lib/rabbitmq/_pool.py +2 -2
- qena_shared_lib/rabbitmq/_publisher.py +3 -3
- qena_shared_lib/rabbitmq/_rpc_client.py +28 -22
- qena_shared_lib/scheduler.py +20 -18
- qena_shared_lib/security.py +2 -2
- qena_shared_lib/utils.py +5 -22
- {qena_shared_lib-0.1.10.dist-info → qena_shared_lib-0.1.11.dist-info}/METADATA +2 -2
- qena_shared_lib-0.1.11.dist-info/RECORD +29 -0
- qena_shared_lib-0.1.10.dist-info/RECORD +0 -29
- {qena_shared_lib-0.1.10.dist-info → qena_shared_lib-0.1.11.dist-info}/WHEEL +0 -0
qena_shared_lib/application.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from enum import Enum
|
2
|
-
from typing import TypeVar
|
2
|
+
from typing import Any, TypeVar
|
3
3
|
|
4
4
|
from fastapi import APIRouter, FastAPI
|
5
5
|
from fastapi.exceptions import RequestValidationError
|
@@ -18,6 +18,7 @@ from .http import ControllerBase
|
|
18
18
|
__all__ = [
|
19
19
|
"Builder",
|
20
20
|
"Environment",
|
21
|
+
"FastAPI",
|
21
22
|
]
|
22
23
|
|
23
24
|
D = TypeVar("D")
|
@@ -29,19 +30,19 @@ class Environment(Enum):
|
|
29
30
|
|
30
31
|
|
31
32
|
class Builder:
|
32
|
-
def __init__(self):
|
33
|
+
def __init__(self) -> None:
|
33
34
|
self._environment = Environment.DEVELOPMENT
|
34
35
|
self._debug = False
|
35
36
|
self._title = "Qena shared lib"
|
36
37
|
self._description = "Qena shared tools for microservice"
|
37
38
|
self._version = "0.1.0"
|
38
39
|
self._lifespan = None
|
39
|
-
self._openapi_url = "/openapi.json"
|
40
|
-
self._docs_url = "/docs"
|
41
|
-
self._redoc_url = "/redoc"
|
42
|
-
self._metrics_endpoint = None
|
43
|
-
self._instrumentator = None
|
44
|
-
self._routers = []
|
40
|
+
self._openapi_url: str | None = "/openapi.json"
|
41
|
+
self._docs_url: str | None = "/docs"
|
42
|
+
self._redoc_url: str | None = "/redoc"
|
43
|
+
self._metrics_endpoint: str | None = None
|
44
|
+
self._instrumentator: Instrumentator | None = None
|
45
|
+
self._routers: list[APIRouter] = []
|
45
46
|
self._container = Container()
|
46
47
|
self._built = False
|
47
48
|
|
@@ -107,7 +108,11 @@ class Builder:
|
|
107
108
|
return self
|
108
109
|
|
109
110
|
def with_singleton(
|
110
|
-
self,
|
111
|
+
self,
|
112
|
+
service: type[D],
|
113
|
+
factory: Any = empty,
|
114
|
+
instance: Any = empty,
|
115
|
+
**kwargs: Any,
|
111
116
|
) -> "Builder":
|
112
117
|
self._container.register(
|
113
118
|
service=service,
|
@@ -120,7 +125,7 @@ class Builder:
|
|
120
125
|
return self
|
121
126
|
|
122
127
|
def with_transient(
|
123
|
-
self, service: type[D], factory=empty, **kwargs
|
128
|
+
self, service: type[D], factory: Any = empty, **kwargs: Any
|
124
129
|
) -> "Builder":
|
125
130
|
self._container.register(
|
126
131
|
service=service,
|
@@ -172,7 +177,7 @@ class Builder:
|
|
172
177
|
|
173
178
|
return app
|
174
179
|
|
175
|
-
def _resolve_api_controllers(self, app: FastAPI):
|
180
|
+
def _resolve_api_controllers(self, app: FastAPI) -> None:
|
176
181
|
api_controller_routers = [
|
177
182
|
api_controller.register_route_handlers()
|
178
183
|
for api_controller in self._container.resolve_all(ControllerBase)
|
qena_shared_lib/background.py
CHANGED
@@ -28,8 +28,8 @@ class Background(AsyncEventLoopMixin):
|
|
28
28
|
def __init__(
|
29
29
|
self,
|
30
30
|
logstash: BaseLogstashSender,
|
31
|
-
):
|
32
|
-
self._queue = Queue()
|
31
|
+
) -> None:
|
32
|
+
self._queue: Queue[tuple[BackgroundTask | None, str | None]] = Queue()
|
33
33
|
self._started = False
|
34
34
|
self._stopped = False
|
35
35
|
self._logstash = logstash
|
@@ -38,7 +38,7 @@ class Background(AsyncEventLoopMixin):
|
|
38
38
|
|
39
39
|
async def _task_manager(
|
40
40
|
self, task: BackgroundTask, task_id: str | None = None
|
41
|
-
):
|
41
|
+
) -> None:
|
42
42
|
self._logger.info(
|
43
43
|
"running %s: %s with %s", task_id, task.func.__name__, task.args
|
44
44
|
)
|
@@ -60,29 +60,32 @@ class Background(AsyncEventLoopMixin):
|
|
60
60
|
self._logger.info("finished running %s", task.func.__name__)
|
61
61
|
self._tasks.pop(task_id, None)
|
62
62
|
|
63
|
-
def _run(self, task: BackgroundTask, task_id: str | None = None):
|
63
|
+
def _run(self, task: BackgroundTask, task_id: str | None = None) -> None:
|
64
64
|
if not self._stopped and (
|
65
65
|
task_id is None or task_id not in self._tasks
|
66
66
|
):
|
67
67
|
self.loop.create_task(self._task_manager(task, task_id))
|
68
68
|
|
69
|
-
async def _run_tasks(self):
|
69
|
+
async def _run_tasks(self) -> None:
|
70
70
|
while not self._stopped or not self._queue.empty():
|
71
71
|
task, task_id = await self._queue.get()
|
72
72
|
|
73
73
|
if task is None and task_id is None:
|
74
74
|
break
|
75
75
|
|
76
|
-
|
76
|
+
if task is not None:
|
77
|
+
self._run(task=task, task_id=task_id)
|
77
78
|
|
78
79
|
tasks = [t for _, t in self._tasks.items() if not t.done()]
|
79
80
|
|
80
81
|
await gather(*tasks)
|
81
82
|
|
82
|
-
def add_task(
|
83
|
+
def add_task(
|
84
|
+
self, task: BackgroundTask, task_id: str | None = None
|
85
|
+
) -> None:
|
83
86
|
self._queue.put_nowait((task, task_id))
|
84
87
|
|
85
|
-
def start(self):
|
88
|
+
def start(self) -> None:
|
86
89
|
if self._started:
|
87
90
|
raise RuntimeError("background runner already running")
|
88
91
|
|
@@ -91,7 +94,7 @@ class Background(AsyncEventLoopMixin):
|
|
91
94
|
|
92
95
|
self._started = True
|
93
96
|
|
94
|
-
def stop(self):
|
97
|
+
def stop(self) -> None:
|
95
98
|
if self._stopped:
|
96
99
|
raise RuntimeError("background runner already stopped")
|
97
100
|
|
@@ -99,11 +102,11 @@ class Background(AsyncEventLoopMixin):
|
|
99
102
|
self._queue.put_nowait((None, None))
|
100
103
|
self.BACKGROUND_RUNNER_STATE.state("stopped")
|
101
104
|
|
102
|
-
def is_alive(self, task_id: str):
|
105
|
+
def is_alive(self, task_id: str) -> bool:
|
103
106
|
if task_id in self._tasks and not self._tasks[task_id].done():
|
104
107
|
return True
|
105
108
|
|
106
109
|
return False
|
107
110
|
|
108
|
-
def count(self):
|
111
|
+
def count(self) -> int:
|
109
112
|
return len(self._tasks)
|
@@ -27,11 +27,11 @@ def get_container(app: FastAPI) -> Container:
|
|
27
27
|
def add_service(
|
28
28
|
app: FastAPI,
|
29
29
|
service: type[D],
|
30
|
-
factory=empty,
|
30
|
+
factory: object = empty,
|
31
31
|
instance: D = empty,
|
32
32
|
scope: Scope = Scope.transient,
|
33
|
-
**kwargs,
|
34
|
-
):
|
33
|
+
**kwargs: Any,
|
34
|
+
) -> None:
|
35
35
|
get_container(app).register(
|
36
36
|
service=service,
|
37
37
|
factory=factory,
|
@@ -30,6 +30,7 @@ def handle_http_service_error(
|
|
30
30
|
logger_provider = get_service(app=request.app, service_key=LoggerProvider)
|
31
31
|
logger = logger_provider.get_logger("http.exception_handler")
|
32
32
|
exception_severity = exception.severity or Severity.LOW
|
33
|
+
user_agent = request.headers.get("user-agent", "__unknown__")
|
33
34
|
message = exception.message
|
34
35
|
tags = [
|
35
36
|
"HTTP",
|
@@ -41,6 +42,7 @@ def handle_http_service_error(
|
|
41
42
|
"serviceType": "HTTP",
|
42
43
|
"method": request.method,
|
43
44
|
"path": request.url.path,
|
45
|
+
"userAgent": user_agent,
|
44
46
|
"exception": exception.__class__.__name__,
|
45
47
|
}
|
46
48
|
exc_info = (
|
@@ -165,6 +167,7 @@ def handle_general_http_exception(
|
|
165
167
|
request: Request, exception: Exception
|
166
168
|
) -> Response:
|
167
169
|
logstash = get_service(app=request.app, service_key=BaseLogstashSender)
|
170
|
+
user_agent = request.get("user-agent", "__unknown__")
|
168
171
|
|
169
172
|
logstash.error(
|
170
173
|
message=f"something went wrong on endpoint `{request.method} {request.url.path}`",
|
@@ -178,6 +181,7 @@ def handle_general_http_exception(
|
|
178
181
|
"serviceType": "HTTP",
|
179
182
|
"method": request.method,
|
180
183
|
"path": request.url.path,
|
184
|
+
"userAgent": user_agent,
|
181
185
|
"exception": exception.__class__.__name__,
|
182
186
|
},
|
183
187
|
exception=exception,
|
qena_shared_lib/exceptions.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
+
from fastapi import status
|
5
|
+
|
4
6
|
__all__ = [
|
5
7
|
"BadGateway",
|
6
8
|
"BadRequest",
|
@@ -62,8 +64,8 @@ class Severity(Enum):
|
|
62
64
|
|
63
65
|
|
64
66
|
class ServiceException(Exception):
|
65
|
-
_GENERAL_SEVERITY = None
|
66
|
-
_GENERAL_EXTRACT_EXC_INFO = None
|
67
|
+
_GENERAL_SEVERITY: Severity | None = None
|
68
|
+
_GENERAL_EXTRACT_EXC_INFO: bool | None = None
|
67
69
|
|
68
70
|
def __init__(
|
69
71
|
self,
|
@@ -78,8 +80,10 @@ class ServiceException(Exception):
|
|
78
80
|
|
79
81
|
if severity is not None:
|
80
82
|
self._severity = severity
|
81
|
-
|
83
|
+
elif self._GENERAL_SEVERITY is not None:
|
82
84
|
self._severity = self._GENERAL_SEVERITY
|
85
|
+
else:
|
86
|
+
self._severity = Severity.LOW
|
83
87
|
|
84
88
|
self._tags = tags
|
85
89
|
self._extra = extra
|
@@ -91,8 +95,10 @@ class ServiceException(Exception):
|
|
91
95
|
|
92
96
|
if extract_exc_info is not None:
|
93
97
|
self._extract_exc_info = extract_exc_info
|
94
|
-
|
98
|
+
elif self._GENERAL_EXTRACT_EXC_INFO is not None:
|
95
99
|
self._extract_exc_info = self._GENERAL_EXTRACT_EXC_INFO
|
100
|
+
else:
|
101
|
+
self._extract_exc_info = False
|
96
102
|
|
97
103
|
@property
|
98
104
|
def message(self) -> str:
|
@@ -126,7 +132,7 @@ class ServiceException(Exception):
|
|
126
132
|
|
127
133
|
|
128
134
|
class HTTPServiceError(ServiceException):
|
129
|
-
_GENERAL_STATUS_CODE = None
|
135
|
+
_GENERAL_STATUS_CODE: int | None = None
|
130
136
|
|
131
137
|
def __init__(
|
132
138
|
self,
|
@@ -152,6 +158,7 @@ class HTTPServiceError(ServiceException):
|
|
152
158
|
)
|
153
159
|
|
154
160
|
self._body = body
|
161
|
+
self._status_code: int | None = None
|
155
162
|
|
156
163
|
if status_code is not None:
|
157
164
|
self._status_code = status_code
|
@@ -190,178 +197,179 @@ class HTTPServiceError(ServiceException):
|
|
190
197
|
|
191
198
|
|
192
199
|
class ClientError(HTTPServiceError):
|
193
|
-
_GENERAL_EXTRACT_EXC_INFO = False
|
194
200
|
_GENERAL_SEVERITY = Severity.MEDIUM
|
201
|
+
_GENERAL_EXTRACT_EXC_INFO = False
|
195
202
|
|
196
203
|
|
197
204
|
class BadRequest(ClientError):
|
198
|
-
_GENERAL_STATUS_CODE =
|
205
|
+
_GENERAL_STATUS_CODE = status.HTTP_400_BAD_REQUEST
|
199
206
|
|
200
207
|
|
201
208
|
class Unauthorized(ClientError):
|
202
|
-
_GENERAL_STATUS_CODE =
|
209
|
+
_GENERAL_STATUS_CODE = status.HTTP_401_UNAUTHORIZED
|
203
210
|
|
204
211
|
|
205
212
|
class PaymentRequired(ClientError):
|
206
|
-
_GENERAL_STATUS_CODE =
|
213
|
+
_GENERAL_STATUS_CODE = status.HTTP_402_PAYMENT_REQUIRED
|
207
214
|
|
208
215
|
|
209
216
|
class Forbidden(ClientError):
|
210
|
-
_GENERAL_STATUS_CODE =
|
217
|
+
_GENERAL_STATUS_CODE = status.HTTP_403_FORBIDDEN
|
211
218
|
|
212
219
|
|
213
220
|
class NotFound(ClientError):
|
214
|
-
_GENERAL_STATUS_CODE =
|
221
|
+
_GENERAL_STATUS_CODE = status.HTTP_404_NOT_FOUND
|
215
222
|
|
216
223
|
|
217
224
|
class MethodNotAllowed(ClientError):
|
218
|
-
_GENERAL_STATUS_CODE =
|
225
|
+
_GENERAL_STATUS_CODE = status.HTTP_405_METHOD_NOT_ALLOWED
|
219
226
|
|
220
227
|
|
221
228
|
class NotAcceptable(ClientError):
|
222
|
-
_GENERAL_STATUS_CODE =
|
229
|
+
_GENERAL_STATUS_CODE = status.HTTP_406_NOT_ACCEPTABLE
|
223
230
|
|
224
231
|
|
225
232
|
class ProxyAuthenticationRequired(ClientError):
|
226
|
-
_GENERAL_STATUS_CODE =
|
233
|
+
_GENERAL_STATUS_CODE = status.HTTP_407_PROXY_AUTHENTICATION_REQUIRED
|
227
234
|
|
228
235
|
|
229
236
|
class RequestTimeout(ClientError):
|
230
|
-
_GENERAL_STATUS_CODE =
|
237
|
+
_GENERAL_STATUS_CODE = status.HTTP_408_REQUEST_TIMEOUT
|
231
238
|
|
232
239
|
|
233
240
|
class Conflict(ClientError):
|
234
|
-
_GENERAL_STATUS_CODE =
|
241
|
+
_GENERAL_STATUS_CODE = status.HTTP_409_CONFLICT
|
235
242
|
|
236
243
|
|
237
244
|
class Gone(ClientError):
|
238
|
-
_GENERAL_STATUS_CODE =
|
245
|
+
_GENERAL_STATUS_CODE = status.HTTP_410_GONE
|
239
246
|
|
240
247
|
|
241
248
|
class LengthRequired(ClientError):
|
242
|
-
_GENERAL_STATUS_CODE =
|
249
|
+
_GENERAL_STATUS_CODE = status.HTTP_411_LENGTH_REQUIRED
|
243
250
|
|
244
251
|
|
245
252
|
class PreconditionFailed(ClientError):
|
246
|
-
_GENERAL_STATUS_CODE =
|
253
|
+
_GENERAL_STATUS_CODE = status.HTTP_412_PRECONDITION_FAILED
|
247
254
|
|
248
255
|
|
249
256
|
class PayloadTooLarge(ClientError):
|
250
|
-
_GENERAL_STATUS_CODE =
|
257
|
+
_GENERAL_STATUS_CODE = status.HTTP_413_REQUEST_ENTITY_TOO_LARGE
|
251
258
|
|
252
259
|
|
253
260
|
class URITooLong(ClientError):
|
254
|
-
_GENERAL_STATUS_CODE =
|
261
|
+
_GENERAL_STATUS_CODE = status.HTTP_414_REQUEST_URI_TOO_LONG
|
255
262
|
|
256
263
|
|
257
264
|
class UnsupportedMediaType(ClientError):
|
258
|
-
_GENERAL_STATUS_CODE =
|
265
|
+
_GENERAL_STATUS_CODE = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
|
259
266
|
|
260
267
|
|
261
268
|
class RangeNotSatisfiable(ClientError):
|
262
|
-
_GENERAL_STATUS_CODE =
|
269
|
+
_GENERAL_STATUS_CODE = status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
|
263
270
|
|
264
271
|
|
265
272
|
class ExpectationFailed(ClientError):
|
266
|
-
_GENERAL_STATUS_CODE =
|
273
|
+
_GENERAL_STATUS_CODE = status.HTTP_417_EXPECTATION_FAILED
|
267
274
|
|
268
275
|
|
269
276
|
class IAmATeapot(ClientError):
|
270
|
-
_GENERAL_STATUS_CODE =
|
277
|
+
_GENERAL_STATUS_CODE = status.HTTP_418_IM_A_TEAPOT
|
271
278
|
|
272
279
|
|
273
280
|
class MisdirectedRequest(ClientError):
|
274
|
-
_GENERAL_STATUS_CODE =
|
281
|
+
_GENERAL_STATUS_CODE = status.HTTP_421_MISDIRECTED_REQUEST
|
275
282
|
|
276
283
|
|
277
284
|
class UnprocessableEntity(ClientError):
|
278
|
-
_GENERAL_STATUS_CODE =
|
285
|
+
_GENERAL_STATUS_CODE = status.HTTP_422_UNPROCESSABLE_ENTITY
|
279
286
|
|
280
287
|
|
281
288
|
class Locked(ClientError):
|
282
|
-
_GENERAL_STATUS_CODE =
|
289
|
+
_GENERAL_STATUS_CODE = status.HTTP_423_LOCKED
|
283
290
|
|
284
291
|
|
285
292
|
class FailedDependency(ClientError):
|
286
|
-
_GENERAL_STATUS_CODE =
|
293
|
+
_GENERAL_STATUS_CODE = status.HTTP_424_FAILED_DEPENDENCY
|
287
294
|
|
288
295
|
|
289
296
|
class TooEarly(ClientError):
|
290
|
-
_GENERAL_STATUS_CODE =
|
297
|
+
_GENERAL_STATUS_CODE = status.HTTP_425_TOO_EARLY
|
291
298
|
|
292
299
|
|
293
300
|
class UpgradeRequired(ClientError):
|
294
|
-
_GENERAL_STATUS_CODE =
|
301
|
+
_GENERAL_STATUS_CODE = status.HTTP_426_UPGRADE_REQUIRED
|
295
302
|
|
296
303
|
|
297
304
|
class PreconditionRequired(ClientError):
|
298
|
-
_GENERAL_STATUS_CODE =
|
305
|
+
_GENERAL_STATUS_CODE = status.HTTP_428_PRECONDITION_REQUIRED
|
299
306
|
|
300
307
|
|
301
308
|
class TooManyRequests(ClientError):
|
302
|
-
_GENERAL_STATUS_CODE =
|
309
|
+
_GENERAL_STATUS_CODE = status.HTTP_429_TOO_MANY_REQUESTS
|
303
310
|
|
304
311
|
|
305
312
|
class RequestHeaderFieldsTooLarge(ClientError):
|
306
|
-
_GENERAL_STATUS_CODE =
|
313
|
+
_GENERAL_STATUS_CODE = status.HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
|
307
314
|
|
308
315
|
|
309
316
|
class UnavailableForLegalReasons(ClientError):
|
310
|
-
_GENERAL_STATUS_CODE =
|
317
|
+
_GENERAL_STATUS_CODE = status.HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
|
311
318
|
|
312
319
|
|
313
320
|
class ServerError(HTTPServiceError):
|
314
|
-
_GENERAL_EXTRACT_EXC_INFO = True
|
315
321
|
_GENERAL_SEVERITY = Severity.HIGH
|
322
|
+
_GENERAL_EXTRACT_EXC_INFO = True
|
316
323
|
|
317
324
|
|
318
325
|
class InternalServerError(ServerError):
|
319
|
-
_GENERAL_STATUS_CODE =
|
326
|
+
_GENERAL_STATUS_CODE = status.HTTP_500_INTERNAL_SERVER_ERROR
|
320
327
|
|
321
328
|
|
322
329
|
class NotImplemented(ServerError):
|
323
|
-
_GENERAL_STATUS_CODE =
|
330
|
+
_GENERAL_STATUS_CODE = status.HTTP_501_NOT_IMPLEMENTED
|
324
331
|
|
325
332
|
|
326
333
|
class BadGateway(ServerError):
|
327
|
-
_GENERAL_STATUS_CODE =
|
334
|
+
_GENERAL_STATUS_CODE = status.HTTP_502_BAD_GATEWAY
|
328
335
|
|
329
336
|
|
330
337
|
class ServiceUnavailable(ServerError):
|
331
|
-
_GENERAL_STATUS_CODE =
|
338
|
+
_GENERAL_STATUS_CODE = status.HTTP_503_SERVICE_UNAVAILABLE
|
332
339
|
|
333
340
|
|
334
341
|
class GatewayTimeout(ServerError):
|
335
|
-
_GENERAL_STATUS_CODE =
|
342
|
+
_GENERAL_STATUS_CODE = status.HTTP_504_GATEWAY_TIMEOUT
|
336
343
|
|
337
344
|
|
338
345
|
class HTTPVersionNotSupported(ServerError):
|
339
|
-
_GENERAL_STATUS_CODE =
|
346
|
+
_GENERAL_STATUS_CODE = status.HTTP_505_HTTP_VERSION_NOT_SUPPORTED
|
340
347
|
|
341
348
|
|
342
349
|
class VariantAlsoNegotiates(ServerError):
|
343
|
-
_GENERAL_STATUS_CODE =
|
350
|
+
_GENERAL_STATUS_CODE = status.HTTP_506_VARIANT_ALSO_NEGOTIATES
|
344
351
|
|
345
352
|
|
346
353
|
class InsufficientStorage(ServerError):
|
347
|
-
_GENERAL_STATUS_CODE =
|
354
|
+
_GENERAL_STATUS_CODE = status.HTTP_507_INSUFFICIENT_STORAGE
|
348
355
|
|
349
356
|
|
350
357
|
class LoopDetected(ServerError):
|
351
|
-
_GENERAL_STATUS_CODE =
|
358
|
+
_GENERAL_STATUS_CODE = status.HTTP_508_LOOP_DETECTED
|
352
359
|
|
353
360
|
|
354
361
|
class NotExtended(ServerError):
|
355
|
-
_GENERAL_STATUS_CODE =
|
362
|
+
_GENERAL_STATUS_CODE = status.HTTP_510_NOT_EXTENDED
|
356
363
|
|
357
364
|
|
358
365
|
class NetworkAuthenticationRequired(ServerError):
|
359
|
-
_GENERAL_STATUS_CODE =
|
366
|
+
_GENERAL_STATUS_CODE = status.HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
|
360
367
|
|
361
368
|
|
362
369
|
class RabbitMQServiceException(ServiceException):
|
363
370
|
_GENERAL_SEVERITY = Severity.HIGH
|
364
|
-
|
371
|
+
_GENERAL_EXTRACT_EXC_INFO = True
|
372
|
+
_GENERAL_CODE: int | None = None
|
365
373
|
|
366
374
|
def __init__(
|
367
375
|
self,
|
@@ -385,8 +393,10 @@ class RabbitMQServiceException(ServiceException):
|
|
385
393
|
|
386
394
|
if code is not None:
|
387
395
|
self._code = code
|
396
|
+
elif self._GENERAL_CODE is not None:
|
397
|
+
self._code = self._GENERAL_CODE
|
388
398
|
else:
|
389
|
-
self._code =
|
399
|
+
self._code = 0
|
390
400
|
|
391
401
|
self._data = data
|
392
402
|
|
qena_shared_lib/http.py
CHANGED
@@ -2,23 +2,75 @@ from dataclasses import dataclass
|
|
2
2
|
from enum import Enum
|
3
3
|
from typing import Any, Callable, Sequence, TypeVar
|
4
4
|
|
5
|
-
from fastapi import
|
5
|
+
from fastapi import (
|
6
|
+
APIRouter,
|
7
|
+
BackgroundTasks,
|
8
|
+
Body,
|
9
|
+
Cookie,
|
10
|
+
File,
|
11
|
+
Form,
|
12
|
+
Header,
|
13
|
+
HTTPException,
|
14
|
+
Path,
|
15
|
+
Query,
|
16
|
+
Request,
|
17
|
+
Response,
|
18
|
+
Security,
|
19
|
+
UploadFile,
|
20
|
+
WebSocket,
|
21
|
+
WebSocketDisconnect,
|
22
|
+
WebSocketException,
|
23
|
+
status,
|
24
|
+
)
|
6
25
|
from fastapi.datastructures import Default
|
7
26
|
from fastapi.params import Depends
|
8
|
-
from fastapi.responses import
|
27
|
+
from fastapi.responses import (
|
28
|
+
FileResponse,
|
29
|
+
HTMLResponse,
|
30
|
+
JSONResponse,
|
31
|
+
PlainTextResponse,
|
32
|
+
RedirectResponse,
|
33
|
+
StreamingResponse,
|
34
|
+
)
|
9
35
|
from fastapi.types import IncEx
|
10
36
|
|
11
37
|
__all__ = [
|
12
38
|
"api_controller",
|
39
|
+
"APIRouter",
|
40
|
+
"BackgroundTasks",
|
41
|
+
"Body",
|
13
42
|
"ControllerBase",
|
43
|
+
"Cookie",
|
14
44
|
"delete",
|
45
|
+
"Depends",
|
46
|
+
"File",
|
47
|
+
"FileResponse",
|
48
|
+
"Form",
|
15
49
|
"get",
|
16
50
|
"head",
|
51
|
+
"Header",
|
52
|
+
"HTMLResponse",
|
53
|
+
"HTTPException",
|
54
|
+
"JSONResponse",
|
17
55
|
"options",
|
18
56
|
"patch",
|
57
|
+
"Path",
|
58
|
+
"PlainTextResponse",
|
19
59
|
"post",
|
20
60
|
"put",
|
61
|
+
"Query",
|
62
|
+
"RedirectResponse",
|
63
|
+
"Request",
|
64
|
+
"Response",
|
65
|
+
"Response",
|
66
|
+
"Security",
|
67
|
+
"status",
|
68
|
+
"StreamingResponse",
|
21
69
|
"trace",
|
70
|
+
"UploadFile",
|
71
|
+
"WebSocket",
|
72
|
+
"WebSocketDisconnect",
|
73
|
+
"WebSocketException",
|
22
74
|
]
|
23
75
|
|
24
76
|
AC = TypeVar("AC")
|
@@ -73,7 +125,9 @@ def api_controller(
|
|
73
125
|
deprecated: bool | None = None,
|
74
126
|
include_in_schema: bool = True,
|
75
127
|
) -> Callable[[type["ControllerBase"]], type["ControllerBase"]]:
|
76
|
-
def annotate_class(
|
128
|
+
def annotate_class(
|
129
|
+
api_controller_class: type[ControllerBase],
|
130
|
+
) -> type[ControllerBase]:
|
77
131
|
router = APIRouter(
|
78
132
|
prefix=prefix or "",
|
79
133
|
tags=tags,
|
qena_shared_lib/logging.py
CHANGED
@@ -18,7 +18,7 @@ ROOT_LOGGER_NAME = environ.get("LOGGER_NAME") or "qena_shared_lib"
|
|
18
18
|
|
19
19
|
|
20
20
|
class LoggerProvider:
|
21
|
-
def __init__(self):
|
21
|
+
def __init__(self) -> None:
|
22
22
|
logger = self.get_logger()
|
23
23
|
|
24
24
|
logger.setLevel(INFO)
|
@@ -55,9 +55,9 @@ class LoggerProvider:
|
|
55
55
|
|
56
56
|
def _check_handler(
|
57
57
|
self, handlers: list[type[Handler]], logger: Optional[Logger] = None
|
58
|
-
):
|
58
|
+
) -> None:
|
59
59
|
if logger is None:
|
60
|
-
return
|
60
|
+
return
|
61
61
|
|
62
62
|
handlers.extend([handler.__class__ for handler in logger.handlers])
|
63
63
|
self._check_handler(handlers=handlers, logger=logger.parent)
|