nexo-schemas 0.0.16__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.
Files changed (69) hide show
  1. nexo/schemas/__init__.py +0 -0
  2. nexo/schemas/application.py +292 -0
  3. nexo/schemas/connection.py +134 -0
  4. nexo/schemas/data.py +27 -0
  5. nexo/schemas/document.py +237 -0
  6. nexo/schemas/error/__init__.py +476 -0
  7. nexo/schemas/error/constants.py +50 -0
  8. nexo/schemas/error/descriptor.py +354 -0
  9. nexo/schemas/error/enums.py +40 -0
  10. nexo/schemas/error/metadata.py +15 -0
  11. nexo/schemas/error/spec.py +312 -0
  12. nexo/schemas/exception/__init__.py +0 -0
  13. nexo/schemas/exception/exc.py +911 -0
  14. nexo/schemas/exception/factory.py +1928 -0
  15. nexo/schemas/exception/handlers.py +110 -0
  16. nexo/schemas/google.py +14 -0
  17. nexo/schemas/key/__init__.py +0 -0
  18. nexo/schemas/key/rsa.py +131 -0
  19. nexo/schemas/metadata.py +21 -0
  20. nexo/schemas/mixins/__init__.py +0 -0
  21. nexo/schemas/mixins/filter.py +140 -0
  22. nexo/schemas/mixins/general.py +65 -0
  23. nexo/schemas/mixins/hierarchy.py +19 -0
  24. nexo/schemas/mixins/identity.py +387 -0
  25. nexo/schemas/mixins/parameter.py +50 -0
  26. nexo/schemas/mixins/service.py +40 -0
  27. nexo/schemas/mixins/sort.py +111 -0
  28. nexo/schemas/mixins/timestamp.py +192 -0
  29. nexo/schemas/model.py +240 -0
  30. nexo/schemas/operation/__init__.py +0 -0
  31. nexo/schemas/operation/action/__init__.py +9 -0
  32. nexo/schemas/operation/action/base.py +14 -0
  33. nexo/schemas/operation/action/resource.py +371 -0
  34. nexo/schemas/operation/action/status.py +8 -0
  35. nexo/schemas/operation/action/system.py +6 -0
  36. nexo/schemas/operation/action/websocket.py +6 -0
  37. nexo/schemas/operation/base.py +289 -0
  38. nexo/schemas/operation/constants.py +18 -0
  39. nexo/schemas/operation/context.py +68 -0
  40. nexo/schemas/operation/dependency.py +26 -0
  41. nexo/schemas/operation/enums.py +168 -0
  42. nexo/schemas/operation/extractor.py +36 -0
  43. nexo/schemas/operation/mixins.py +53 -0
  44. nexo/schemas/operation/request.py +1066 -0
  45. nexo/schemas/operation/resource.py +839 -0
  46. nexo/schemas/operation/system.py +55 -0
  47. nexo/schemas/operation/websocket.py +55 -0
  48. nexo/schemas/pagination.py +67 -0
  49. nexo/schemas/parameter.py +60 -0
  50. nexo/schemas/payload.py +116 -0
  51. nexo/schemas/resource.py +64 -0
  52. nexo/schemas/response.py +1041 -0
  53. nexo/schemas/security/__init__.py +0 -0
  54. nexo/schemas/security/api_key.py +63 -0
  55. nexo/schemas/security/authentication.py +848 -0
  56. nexo/schemas/security/authorization.py +922 -0
  57. nexo/schemas/security/enums.py +32 -0
  58. nexo/schemas/security/impersonation.py +179 -0
  59. nexo/schemas/security/token.py +402 -0
  60. nexo/schemas/security/types.py +17 -0
  61. nexo/schemas/success/__init__.py +0 -0
  62. nexo/schemas/success/descriptor.py +100 -0
  63. nexo/schemas/success/enums.py +23 -0
  64. nexo/schemas/user_agent.py +46 -0
  65. nexo_schemas-0.0.16.dist-info/METADATA +87 -0
  66. nexo_schemas-0.0.16.dist-info/RECORD +69 -0
  67. nexo_schemas-0.0.16.dist-info/WHEEL +5 -0
  68. nexo_schemas-0.0.16.dist-info/licenses/LICENSE +21 -0
  69. nexo_schemas-0.0.16.dist-info/top_level.txt +1 -0
@@ -0,0 +1,911 @@
1
+ import traceback as tb
2
+ from fastapi.responses import JSONResponse
3
+ from google.cloud.pubsub_v1.publisher.futures import Future
4
+ from logging import Logger
5
+ from typing import Any, Generic, Literal, Type, overload
6
+ from uuid import uuid4
7
+ from nexo.logging.enums import LogLevel
8
+ from nexo.types.boolean import OptBool
9
+ from nexo.types.dict import OptStrToStrDict
10
+ from nexo.types.string import ListOfStrs, OptStr
11
+ from nexo.types.uuid import OptUUID
12
+ from ..application import ApplicationContext, OptApplicationContext
13
+ from ..connection import ConnectionContext, OptConnectionContext
14
+ from ..error.enums import ErrorCode
15
+ from ..error.metadata import ErrorMetadata
16
+ from ..error import (
17
+ BadRequestError,
18
+ UnauthorizedError,
19
+ ForbiddenError,
20
+ NotFoundError,
21
+ MethodNotAllowedError,
22
+ ConflictError,
23
+ UnprocessableEntityError,
24
+ TooManyRequestsError,
25
+ InternalServerError as InternalServerErrorSchema,
26
+ NotImplementedError,
27
+ BadGatewayError,
28
+ ServiceUnavailableError,
29
+ AnyErrorT,
30
+ )
31
+ from ..google import ListOfPublisherHandlers
32
+ from ..operation.action.resource import (
33
+ ResourceOperationActions,
34
+ AnyResourceOperationAction,
35
+ )
36
+ from ..operation.action.system import SystemOperationAction
37
+ from ..operation.action.websocket import WebSocketOperationAction
38
+ from ..operation.context import Context
39
+ from ..operation.enums import OperationType
40
+ from ..operation.mixins import Timestamp, OptTimestamp
41
+ from ..operation.request import (
42
+ CreateFailedRequestOperation,
43
+ ReadFailedRequestOperation,
44
+ UpdateFailedRequestOperation,
45
+ DeleteFailedRequestOperation,
46
+ FailedRequestOperationFactory,
47
+ )
48
+ from ..operation.resource import (
49
+ CreateFailedResourceOperation,
50
+ ReadFailedResourceOperation,
51
+ UpdateFailedResourceOperation,
52
+ DeleteFailedResourceOperation,
53
+ FailedResourceOperationFactory,
54
+ )
55
+ from ..operation.system import FailedSystemOperation
56
+ from ..operation.websocket import FailedWebSocketOperation
57
+ from ..resource import Resource, OptResource
58
+ from ..response import (
59
+ BadRequestResponse,
60
+ UnauthorizedResponse,
61
+ ForbiddenResponse,
62
+ NotFoundResponse,
63
+ MethodNotAllowedResponse,
64
+ ConflictResponse,
65
+ UnprocessableEntityResponse,
66
+ TooManyRequestsResponse,
67
+ InternalServerErrorResponse,
68
+ NotImplementedResponse,
69
+ BadGatewayResponse,
70
+ ServiceUnavailableResponse,
71
+ OptAnyErrorResponse,
72
+ ErrorResponseT,
73
+ ResponseContext,
74
+ )
75
+ from ..security.authentication import OptAnyAuthentication
76
+ from ..security.authorization import OptAnyAuthorization
77
+ from ..security.impersonation import OptImpersonation
78
+
79
+
80
+ class MaleoException(
81
+ Exception,
82
+ Generic[
83
+ AnyErrorT,
84
+ ErrorResponseT,
85
+ ],
86
+ ):
87
+ error_cls: Type[AnyErrorT]
88
+ response_cls: Type[ErrorResponseT]
89
+
90
+ @overload
91
+ def __init__(
92
+ self,
93
+ *args: object,
94
+ details: Any = None,
95
+ operation_type: Literal[OperationType.REQUEST],
96
+ application_context: OptApplicationContext = None,
97
+ operation_id: OptUUID = None,
98
+ operation_context: Context,
99
+ operation_action: AnyResourceOperationAction,
100
+ operation_timestamp: OptTimestamp = None,
101
+ operation_summary: OptStr = None,
102
+ connection_context: ConnectionContext,
103
+ authentication: OptAnyAuthentication = None,
104
+ authorization: OptAnyAuthorization = None,
105
+ impersonation: OptImpersonation = None,
106
+ response: OptAnyErrorResponse = None,
107
+ ) -> None: ...
108
+ @overload
109
+ def __init__(
110
+ self,
111
+ *args: object,
112
+ details: Any = None,
113
+ operation_type: Literal[OperationType.RESOURCE],
114
+ application_context: OptApplicationContext = None,
115
+ operation_id: OptUUID = None,
116
+ operation_context: Context,
117
+ operation_action: AnyResourceOperationAction,
118
+ resource: Resource,
119
+ operation_timestamp: OptTimestamp = None,
120
+ operation_summary: OptStr = None,
121
+ connection_context: OptConnectionContext = None,
122
+ authentication: OptAnyAuthentication = None,
123
+ authorization: OptAnyAuthorization = None,
124
+ impersonation: OptImpersonation = None,
125
+ response: OptAnyErrorResponse = None,
126
+ ) -> None: ...
127
+ @overload
128
+ def __init__(
129
+ self,
130
+ *args: object,
131
+ details: Any = None,
132
+ operation_type: Literal[OperationType.REQUEST, OperationType.RESOURCE],
133
+ application_context: OptApplicationContext = None,
134
+ operation_id: OptUUID = None,
135
+ operation_context: Context,
136
+ operation_action: AnyResourceOperationAction,
137
+ resource: OptResource = None,
138
+ operation_timestamp: OptTimestamp = None,
139
+ operation_summary: OptStr = None,
140
+ connection_context: OptConnectionContext = None,
141
+ authentication: OptAnyAuthentication = None,
142
+ authorization: OptAnyAuthorization = None,
143
+ impersonation: OptImpersonation = None,
144
+ response: OptAnyErrorResponse = None,
145
+ ) -> None: ...
146
+ @overload
147
+ def __init__(
148
+ self,
149
+ *args: object,
150
+ details: Any = None,
151
+ operation_type: Literal[OperationType.SYSTEM],
152
+ application_context: OptApplicationContext = None,
153
+ operation_id: OptUUID = None,
154
+ operation_context: Context,
155
+ operation_action: SystemOperationAction,
156
+ operation_timestamp: OptTimestamp = None,
157
+ operation_summary: OptStr = None,
158
+ connection_context: OptConnectionContext = None,
159
+ authentication: OptAnyAuthentication = None,
160
+ authorization: OptAnyAuthorization = None,
161
+ impersonation: OptImpersonation = None,
162
+ response: OptAnyErrorResponse = None,
163
+ ) -> None: ...
164
+ @overload
165
+ def __init__(
166
+ self,
167
+ *args: object,
168
+ details: Any = None,
169
+ operation_type: Literal[OperationType.WEBSOCKET],
170
+ application_context: OptApplicationContext = None,
171
+ operation_id: OptUUID = None,
172
+ operation_context: Context,
173
+ operation_action: WebSocketOperationAction,
174
+ operation_timestamp: OptTimestamp = None,
175
+ operation_summary: OptStr = None,
176
+ connection_context: OptConnectionContext = None,
177
+ authentication: OptAnyAuthentication = None,
178
+ authorization: OptAnyAuthorization = None,
179
+ impersonation: OptImpersonation = None,
180
+ response: OptAnyErrorResponse = None,
181
+ ) -> None: ...
182
+ @overload
183
+ def __init__(
184
+ self,
185
+ *args: object,
186
+ details: Any = None,
187
+ operation_type: OperationType,
188
+ application_context: OptApplicationContext = None,
189
+ operation_id: OptUUID = None,
190
+ operation_context: Context,
191
+ operation_action: (
192
+ AnyResourceOperationAction
193
+ | SystemOperationAction
194
+ | WebSocketOperationAction
195
+ ),
196
+ resource: OptResource = None,
197
+ operation_timestamp: OptTimestamp = None,
198
+ operation_summary: OptStr = None,
199
+ connection_context: OptConnectionContext = None,
200
+ authentication: OptAnyAuthentication = None,
201
+ authorization: OptAnyAuthorization = None,
202
+ impersonation: OptImpersonation = None,
203
+ response: OptAnyErrorResponse = None,
204
+ ) -> None: ...
205
+ def __init__(
206
+ self,
207
+ *args: object,
208
+ details: Any = None,
209
+ operation_type: OperationType,
210
+ application_context: OptApplicationContext = None,
211
+ operation_id: OptUUID = None,
212
+ operation_context: Context,
213
+ operation_action: (
214
+ AnyResourceOperationAction
215
+ | SystemOperationAction
216
+ | WebSocketOperationAction
217
+ ),
218
+ resource: OptResource = None,
219
+ operation_timestamp: OptTimestamp = None,
220
+ operation_summary: OptStr = None,
221
+ connection_context: OptConnectionContext = None,
222
+ authentication: OptAnyAuthentication = None,
223
+ authorization: OptAnyAuthorization = None,
224
+ impersonation: OptImpersonation = None,
225
+ response: OptAnyErrorResponse = None,
226
+ ) -> None:
227
+ super().__init__(*args)
228
+ self.details = details
229
+ self.operation_type = operation_type
230
+
231
+ self.application_context = (
232
+ application_context
233
+ if application_context is not None
234
+ else ApplicationContext.new()
235
+ )
236
+
237
+ self.operation_id = operation_id if operation_id is not None else uuid4()
238
+ self.operation_context = operation_context
239
+ self.operation_action = operation_action
240
+ self.resource = resource
241
+
242
+ self.operation_timestamp = (
243
+ operation_timestamp if operation_timestamp is not None else Timestamp.now()
244
+ )
245
+
246
+ self.operation_summary = (
247
+ operation_summary
248
+ if operation_summary is not None
249
+ else f"{self.operation_type.capitalize()} operation failed due to exception being raised"
250
+ )
251
+
252
+ self.connection_context = connection_context
253
+ self.authentication = authentication
254
+ self.authorization = authorization
255
+ self.impersonation = impersonation
256
+ self._logged: bool = False
257
+ self._published: bool = False
258
+ self._futures: list[Future] = []
259
+
260
+ if response is not None:
261
+ self.response: ErrorResponseT = self.response_cls.model_validate(
262
+ response.model_dump()
263
+ )
264
+ if self.response.other is None and self.details is not None:
265
+ self.response.other = self.details
266
+ else:
267
+ self.response: ErrorResponseT = self.response_cls(other=self.details)
268
+
269
+ self.error: AnyErrorT = self.error_cls(
270
+ metadata=ErrorMetadata(details=self.details, traceback=self.traceback)
271
+ )
272
+
273
+ @property
274
+ def traceback(self) -> ListOfStrs:
275
+ return tb.format_exception(self)
276
+
277
+ @property
278
+ def operation(self) -> (
279
+ CreateFailedRequestOperation[
280
+ AnyErrorT,
281
+ ErrorResponseT,
282
+ ]
283
+ | ReadFailedRequestOperation[
284
+ AnyErrorT,
285
+ ErrorResponseT,
286
+ ]
287
+ | UpdateFailedRequestOperation[
288
+ AnyErrorT,
289
+ ErrorResponseT,
290
+ ]
291
+ | DeleteFailedRequestOperation[
292
+ AnyErrorT,
293
+ ErrorResponseT,
294
+ ]
295
+ | CreateFailedResourceOperation[
296
+ AnyErrorT,
297
+ ErrorResponseT,
298
+ ]
299
+ | ReadFailedResourceOperation[
300
+ AnyErrorT,
301
+ ErrorResponseT,
302
+ ]
303
+ | UpdateFailedResourceOperation[
304
+ AnyErrorT,
305
+ ErrorResponseT,
306
+ ]
307
+ | DeleteFailedResourceOperation[
308
+ AnyErrorT,
309
+ ErrorResponseT,
310
+ ]
311
+ | FailedSystemOperation[
312
+ AnyErrorT,
313
+ ErrorResponseT,
314
+ ]
315
+ | FailedWebSocketOperation[
316
+ AnyErrorT,
317
+ ErrorResponseT,
318
+ ]
319
+ ):
320
+ if self.operation_type is OperationType.REQUEST:
321
+ if not isinstance(self.operation_action, ResourceOperationActions):
322
+ raise ValueError(
323
+ ErrorCode.BAD_REQUEST,
324
+ f"Invalid operation_action to generate request operation: {type(self.operation_action)}",
325
+ )
326
+ if self.connection_context is None:
327
+ raise ValueError(
328
+ ErrorCode.BAD_REQUEST,
329
+ "Failed generating request operation from MaleoException. Request context is not given",
330
+ )
331
+ response = JSONResponse(
332
+ content=self.response.model_dump(mode="json"),
333
+ status_code=self.error.spec.status_code,
334
+ )
335
+ response_context = ResponseContext(
336
+ status_code=response.status_code,
337
+ media_type=response.media_type,
338
+ headers=response.headers.items(),
339
+ )
340
+
341
+ return FailedRequestOperationFactory[
342
+ AnyErrorT,
343
+ ErrorResponseT,
344
+ ].generate(
345
+ self.operation_action,
346
+ application_context=self.application_context,
347
+ id=self.operation_id,
348
+ context=self.operation_context,
349
+ timestamp=self.operation_timestamp,
350
+ summary=self.operation_summary,
351
+ error=self.error,
352
+ connection_context=self.connection_context,
353
+ authentication=self.authentication,
354
+ authorization=self.authorization,
355
+ impersonation=self.impersonation,
356
+ response=self.response,
357
+ response_context=response_context,
358
+ )
359
+ elif self.operation_type is OperationType.RESOURCE:
360
+ if not isinstance(self.operation_action, ResourceOperationActions):
361
+ raise ValueError(
362
+ ErrorCode.BAD_REQUEST,
363
+ f"Invalid operation_action to generate resource operation: {type(self.operation_action)}",
364
+ )
365
+ if self.resource is None:
366
+ raise ValueError(
367
+ ErrorCode.BAD_REQUEST,
368
+ "Failed generating resource operation from MaleoException. Resource is not given",
369
+ )
370
+ return FailedResourceOperationFactory[
371
+ AnyErrorT,
372
+ ErrorResponseT,
373
+ ].generate(
374
+ self.operation_action,
375
+ application_context=self.application_context,
376
+ id=self.operation_id,
377
+ context=self.operation_context,
378
+ timestamp=self.operation_timestamp,
379
+ summary=self.operation_summary,
380
+ error=self.error,
381
+ connection_context=self.connection_context,
382
+ authentication=self.authentication,
383
+ authorization=self.authorization,
384
+ impersonation=self.impersonation,
385
+ resource=self.resource,
386
+ response=self.response,
387
+ )
388
+ elif self.operation_type is OperationType.SYSTEM:
389
+ if not isinstance(self.operation_action, SystemOperationAction):
390
+ raise ValueError(
391
+ ErrorCode.BAD_REQUEST,
392
+ f"Invalid operation_action to generate system operation: {type(self.operation_action)}",
393
+ )
394
+ return FailedSystemOperation[
395
+ AnyErrorT,
396
+ ErrorResponseT,
397
+ ](
398
+ application_context=self.application_context,
399
+ id=self.operation_id,
400
+ context=self.operation_context,
401
+ action=self.operation_action,
402
+ timestamp=self.operation_timestamp,
403
+ summary=self.operation_summary,
404
+ error=self.error,
405
+ connection_context=self.connection_context,
406
+ authentication=self.authentication,
407
+ authorization=self.authorization,
408
+ impersonation=self.impersonation,
409
+ response=self.response,
410
+ )
411
+ elif self.operation_type is OperationType.WEBSOCKET:
412
+ if not isinstance(self.operation_action, WebSocketOperationAction):
413
+ raise ValueError(
414
+ ErrorCode.BAD_REQUEST,
415
+ f"Invalid operation_action to generate websocket operation: {type(self.operation_action)}",
416
+ )
417
+ return FailedWebSocketOperation[
418
+ AnyErrorT,
419
+ ErrorResponseT,
420
+ ](
421
+ application_context=self.application_context,
422
+ id=self.operation_id,
423
+ context=self.operation_context,
424
+ action=self.operation_action,
425
+ timestamp=self.operation_timestamp,
426
+ summary=self.operation_summary,
427
+ error=self.error,
428
+ connection_context=self.connection_context,
429
+ authentication=self.authentication,
430
+ authorization=self.authorization,
431
+ impersonation=self.impersonation,
432
+ response=self.response,
433
+ )
434
+
435
+ raise ValueError(
436
+ ErrorCode.BAD_REQUEST,
437
+ f"Invalid operation_type to generate any operation from maleo exception: {self.operation_type}",
438
+ )
439
+
440
+ @overload
441
+ def generate_operation(
442
+ self,
443
+ operation_type: Literal[OperationType.REQUEST],
444
+ /,
445
+ ) -> (
446
+ CreateFailedRequestOperation[
447
+ AnyErrorT,
448
+ ErrorResponseT,
449
+ ]
450
+ | ReadFailedRequestOperation[
451
+ AnyErrorT,
452
+ ErrorResponseT,
453
+ ]
454
+ | UpdateFailedRequestOperation[
455
+ AnyErrorT,
456
+ ErrorResponseT,
457
+ ]
458
+ | DeleteFailedRequestOperation[
459
+ AnyErrorT,
460
+ ErrorResponseT,
461
+ ]
462
+ ): ...
463
+ @overload
464
+ def generate_operation(
465
+ self,
466
+ operation_type: Literal[OperationType.RESOURCE],
467
+ /,
468
+ ) -> (
469
+ CreateFailedResourceOperation[
470
+ AnyErrorT,
471
+ ErrorResponseT,
472
+ ]
473
+ | ReadFailedResourceOperation[
474
+ AnyErrorT,
475
+ ErrorResponseT,
476
+ ]
477
+ | UpdateFailedResourceOperation[
478
+ AnyErrorT,
479
+ ErrorResponseT,
480
+ ]
481
+ | DeleteFailedResourceOperation[
482
+ AnyErrorT,
483
+ ErrorResponseT,
484
+ ]
485
+ ): ...
486
+ @overload
487
+ def generate_operation(
488
+ self,
489
+ operation_type: Literal[OperationType.SYSTEM],
490
+ /,
491
+ ) -> FailedSystemOperation[AnyErrorT, ErrorResponseT]: ...
492
+ @overload
493
+ def generate_operation(
494
+ self,
495
+ operation_type: Literal[OperationType.WEBSOCKET],
496
+ /,
497
+ ) -> FailedWebSocketOperation[AnyErrorT, ErrorResponseT]: ...
498
+ def generate_operation(
499
+ self,
500
+ operation_type: OperationType,
501
+ /,
502
+ ) -> (
503
+ CreateFailedRequestOperation[
504
+ AnyErrorT,
505
+ ErrorResponseT,
506
+ ]
507
+ | ReadFailedRequestOperation[
508
+ AnyErrorT,
509
+ ErrorResponseT,
510
+ ]
511
+ | UpdateFailedRequestOperation[
512
+ AnyErrorT,
513
+ ErrorResponseT,
514
+ ]
515
+ | DeleteFailedRequestOperation[
516
+ AnyErrorT,
517
+ ErrorResponseT,
518
+ ]
519
+ | CreateFailedResourceOperation[
520
+ AnyErrorT,
521
+ ErrorResponseT,
522
+ ]
523
+ | ReadFailedResourceOperation[
524
+ AnyErrorT,
525
+ ErrorResponseT,
526
+ ]
527
+ | UpdateFailedResourceOperation[
528
+ AnyErrorT,
529
+ ErrorResponseT,
530
+ ]
531
+ | DeleteFailedResourceOperation[
532
+ AnyErrorT,
533
+ ErrorResponseT,
534
+ ]
535
+ | FailedSystemOperation[
536
+ AnyErrorT,
537
+ ErrorResponseT,
538
+ ]
539
+ | FailedWebSocketOperation[
540
+ AnyErrorT,
541
+ ErrorResponseT,
542
+ ]
543
+ ):
544
+ if operation_type != self.operation_type:
545
+ raise ValueError(
546
+ ErrorCode.INTERNAL_SERVER_ERROR,
547
+ (
548
+ "Failed generating operation for MaleoException ",
549
+ "due to mismatched operation_type. ",
550
+ f"Expected '{self.operation_type}' ",
551
+ f"but received {operation_type}",
552
+ ),
553
+ )
554
+
555
+ if operation_type is OperationType.SYSTEM:
556
+ if not isinstance(self.operation_action, SystemOperationAction):
557
+ raise ValueError(
558
+ ErrorCode.BAD_REQUEST,
559
+ f"Invalid operation_action to generate system operation: {type(self.operation_action)}",
560
+ )
561
+ return FailedSystemOperation[
562
+ AnyErrorT,
563
+ ErrorResponseT,
564
+ ](
565
+ application_context=self.application_context,
566
+ id=self.operation_id,
567
+ context=self.operation_context,
568
+ action=self.operation_action,
569
+ timestamp=self.operation_timestamp,
570
+ summary=self.operation_summary,
571
+ error=self.error,
572
+ connection_context=self.connection_context,
573
+ authentication=self.authentication,
574
+ authorization=self.authorization,
575
+ impersonation=self.impersonation,
576
+ response=self.response,
577
+ )
578
+ elif operation_type is OperationType.WEBSOCKET:
579
+ if not isinstance(self.operation_action, WebSocketOperationAction):
580
+ raise ValueError(
581
+ ErrorCode.BAD_REQUEST,
582
+ f"Invalid operation_action to generate websocket operation: {type(self.operation_action)}",
583
+ )
584
+ return FailedWebSocketOperation[
585
+ AnyErrorT,
586
+ ErrorResponseT,
587
+ ](
588
+ application_context=self.application_context,
589
+ id=self.operation_id,
590
+ context=self.operation_context,
591
+ action=self.operation_action,
592
+ timestamp=self.operation_timestamp,
593
+ summary=self.operation_summary,
594
+ error=self.error,
595
+ connection_context=self.connection_context,
596
+ authentication=self.authentication,
597
+ authorization=self.authorization,
598
+ impersonation=self.impersonation,
599
+ response=self.response,
600
+ )
601
+ else:
602
+ if not isinstance(self.operation_action, ResourceOperationActions):
603
+ raise ValueError(
604
+ ErrorCode.BAD_REQUEST,
605
+ f"Invalid operation_action to generate {operation_type} operation: {type(self.operation_action)}",
606
+ )
607
+
608
+ if operation_type is OperationType.REQUEST:
609
+ if self.connection_context is None:
610
+ raise ValueError(
611
+ ErrorCode.BAD_REQUEST,
612
+ "Failed generating request operation from MaleoException. Request context is not given",
613
+ )
614
+ response = JSONResponse(
615
+ content=self.response.model_dump(mode="json"),
616
+ status_code=self.error.spec.status_code,
617
+ )
618
+ response_context = ResponseContext(
619
+ status_code=response.status_code,
620
+ media_type=response.media_type,
621
+ headers=response.headers.items(),
622
+ )
623
+
624
+ return FailedRequestOperationFactory[
625
+ AnyErrorT,
626
+ ErrorResponseT,
627
+ ].generate(
628
+ self.operation_action,
629
+ application_context=self.application_context,
630
+ id=self.operation_id,
631
+ context=self.operation_context,
632
+ timestamp=self.operation_timestamp,
633
+ summary=self.operation_summary,
634
+ error=self.error,
635
+ connection_context=self.connection_context,
636
+ authentication=self.authentication,
637
+ authorization=self.authorization,
638
+ impersonation=self.impersonation,
639
+ response=self.response,
640
+ response_context=response_context,
641
+ )
642
+ elif operation_type is OperationType.RESOURCE:
643
+ if self.resource is None:
644
+ raise ValueError(
645
+ ErrorCode.BAD_REQUEST,
646
+ "Failed generating resource operation from MaleoException. Resource is not given",
647
+ )
648
+ return FailedResourceOperationFactory[
649
+ AnyErrorT,
650
+ ErrorResponseT,
651
+ ].generate(
652
+ self.operation_action,
653
+ application_context=self.application_context,
654
+ id=self.operation_id,
655
+ context=self.operation_context,
656
+ timestamp=self.operation_timestamp,
657
+ summary=self.operation_summary,
658
+ error=self.error,
659
+ connection_context=self.connection_context,
660
+ authentication=self.authentication,
661
+ authorization=self.authorization,
662
+ impersonation=self.impersonation,
663
+ resource=self.resource,
664
+ response=self.response,
665
+ )
666
+
667
+ def log_operation(
668
+ self,
669
+ logger: Logger,
670
+ level: LogLevel = LogLevel.ERROR,
671
+ *,
672
+ exc_info: OptBool = None,
673
+ additional_extra: OptStrToStrDict = None,
674
+ override_extra: OptStrToStrDict = None,
675
+ additional_labels: OptStrToStrDict = None,
676
+ override_labels: OptStrToStrDict = None,
677
+ ):
678
+ if not self._logged:
679
+ self.operation.log(
680
+ logger,
681
+ level,
682
+ exc_info=exc_info,
683
+ additional_extra=additional_extra,
684
+ override_extra=override_extra,
685
+ additional_labels=additional_labels,
686
+ override_labels=override_labels,
687
+ )
688
+ self._logged = True
689
+
690
+ def publish_operation(
691
+ self,
692
+ logger: Logger,
693
+ publishers: ListOfPublisherHandlers = [],
694
+ *,
695
+ additional_labels: OptStrToStrDict = None,
696
+ override_labels: OptStrToStrDict = None,
697
+ ) -> list[Future]:
698
+ if not self._published:
699
+ futures = self.operation.publish(
700
+ logger,
701
+ publishers,
702
+ additional_labels=additional_labels,
703
+ override_labels=override_labels,
704
+ )
705
+ self._published = True
706
+ self._futures = futures
707
+ return self._futures
708
+
709
+ def log_and_publish_operation(
710
+ self,
711
+ logger: Logger,
712
+ publishers: ListOfPublisherHandlers = [],
713
+ level: LogLevel = LogLevel.ERROR,
714
+ *,
715
+ exc_info: OptBool = None,
716
+ additional_extra: OptStrToStrDict = None,
717
+ override_extra: OptStrToStrDict = None,
718
+ additional_labels: OptStrToStrDict = None,
719
+ override_labels: OptStrToStrDict = None,
720
+ ) -> list[Future]:
721
+ self.log_operation(
722
+ logger,
723
+ level,
724
+ exc_info=exc_info,
725
+ additional_extra=additional_extra,
726
+ override_extra=override_extra,
727
+ additional_labels=additional_labels,
728
+ override_labels=override_labels,
729
+ )
730
+ return self.publish_operation(
731
+ logger,
732
+ publishers,
733
+ additional_labels=additional_labels,
734
+ override_labels=override_labels,
735
+ )
736
+
737
+
738
+ class ClientException(
739
+ MaleoException[
740
+ AnyErrorT,
741
+ ErrorResponseT,
742
+ ],
743
+ Generic[
744
+ AnyErrorT,
745
+ ErrorResponseT,
746
+ ],
747
+ ):
748
+ """Base class for all client error (HTTP 4xx) responses"""
749
+
750
+
751
+ class BadRequest(
752
+ ClientException[
753
+ BadRequestError,
754
+ BadRequestResponse,
755
+ ]
756
+ ):
757
+ error_cls = BadRequestError
758
+ response_cls = BadRequestResponse
759
+
760
+
761
+ class Unauthorized(
762
+ ClientException[
763
+ UnauthorizedError,
764
+ UnauthorizedResponse,
765
+ ]
766
+ ):
767
+ error_cls = UnauthorizedError
768
+ response_cls = UnauthorizedResponse
769
+
770
+
771
+ class Forbidden(
772
+ ClientException[
773
+ ForbiddenError,
774
+ ForbiddenResponse,
775
+ ]
776
+ ):
777
+ error_cls = ForbiddenError
778
+ response_cls = ForbiddenResponse
779
+
780
+
781
+ class NotFound(
782
+ ClientException[
783
+ NotFoundError,
784
+ NotFoundResponse,
785
+ ]
786
+ ):
787
+ error_cls = NotFoundError
788
+ response_cls = NotFoundResponse
789
+
790
+
791
+ class MethodNotAllowed(
792
+ ClientException[
793
+ MethodNotAllowedError,
794
+ MethodNotAllowedResponse,
795
+ ]
796
+ ):
797
+ error_cls = MethodNotAllowedError
798
+ response_cls = MethodNotAllowedResponse
799
+
800
+
801
+ class Conflict(
802
+ ClientException[
803
+ ConflictError,
804
+ ConflictResponse,
805
+ ]
806
+ ):
807
+ error_cls = ConflictError
808
+ response_cls = ConflictResponse
809
+
810
+
811
+ class UnprocessableEntity(
812
+ ClientException[
813
+ UnprocessableEntityError,
814
+ UnprocessableEntityResponse,
815
+ ]
816
+ ):
817
+ error_cls = UnprocessableEntityError
818
+ response_cls = UnprocessableEntityResponse
819
+
820
+
821
+ class TooManyRequests(
822
+ ClientException[
823
+ TooManyRequestsError,
824
+ TooManyRequestsResponse,
825
+ ]
826
+ ):
827
+ error_cls = TooManyRequestsError
828
+ response_cls = TooManyRequestsResponse
829
+
830
+
831
+ class ServerException(
832
+ MaleoException[
833
+ AnyErrorT,
834
+ ErrorResponseT,
835
+ ],
836
+ Generic[
837
+ AnyErrorT,
838
+ ErrorResponseT,
839
+ ],
840
+ ):
841
+ """Base class for all server error (HTTP 5xx) responses"""
842
+
843
+
844
+ class InternalServerError(
845
+ ServerException[
846
+ InternalServerErrorSchema,
847
+ InternalServerErrorResponse,
848
+ ]
849
+ ):
850
+ error_cls = InternalServerErrorSchema
851
+ response_cls = InternalServerErrorResponse
852
+
853
+
854
+ class NotImplemented(
855
+ ServerException[
856
+ NotImplementedError,
857
+ NotImplementedResponse,
858
+ ]
859
+ ):
860
+ error_cls = NotImplementedError
861
+ response_cls = NotImplementedResponse
862
+
863
+
864
+ class BadGateway(
865
+ ServerException[
866
+ BadGatewayError,
867
+ BadGatewayResponse,
868
+ ]
869
+ ):
870
+ error_cls = BadGatewayError
871
+ response_cls = BadGatewayResponse
872
+
873
+
874
+ class ServiceUnavailable(
875
+ ServerException[
876
+ ServiceUnavailableError,
877
+ ServiceUnavailableResponse,
878
+ ]
879
+ ):
880
+ error_cls = ServiceUnavailableError
881
+ response_cls = ServiceUnavailableResponse
882
+
883
+
884
+ AnyExceptionType = (
885
+ Type[BadRequest]
886
+ | Type[Unauthorized]
887
+ | Type[Forbidden]
888
+ | Type[NotFound]
889
+ | Type[MethodNotAllowed]
890
+ | Type[Conflict]
891
+ | Type[UnprocessableEntity]
892
+ | Type[TooManyRequests]
893
+ | Type[InternalServerError]
894
+ | Type[NotImplemented]
895
+ | Type[BadGateway]
896
+ | Type[ServiceUnavailable]
897
+ )
898
+ AnyException = (
899
+ BadRequest
900
+ | Unauthorized
901
+ | Forbidden
902
+ | NotFound
903
+ | MethodNotAllowed
904
+ | Conflict
905
+ | UnprocessableEntity
906
+ | TooManyRequests
907
+ | InternalServerError
908
+ | NotImplemented
909
+ | BadGateway
910
+ | ServiceUnavailable
911
+ )