maleo-schemas 0.0.1__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.
@@ -0,0 +1,853 @@
1
+ import re
2
+ from fastapi import Request
3
+ from pydantic import BaseModel, Field
4
+ from typing import Generic, Literal, Optional, TypeVar, Union, overload
5
+ from uuid import UUID
6
+ from maleo.enums.operation import (
7
+ OperationType,
8
+ ResourceOperationType,
9
+ ResourceOperationCreateType,
10
+ ResourceOperationUpdateType,
11
+ ResourceOperationDataUpdateType,
12
+ ResourceOperationStatusUpdateType,
13
+ )
14
+ from maleo.mixins.general import SuccessT
15
+ from maleo.mixins.timestamp import OperationTimestamp
16
+ from maleo.dtos.authentication import AuthenticationT
17
+ from maleo.dtos.data import DataT
18
+ from maleo.dtos.contexts.operation import OperationContext
19
+ from maleo.dtos.contexts.request import RequestContext
20
+ from maleo.dtos.contexts.service import ServiceContext
21
+ from maleo.dtos.error import GenericErrorT, ErrorT
22
+ from maleo.dtos.metadata import MetadataT
23
+ from maleo.dtos.pagination import PaginationT
24
+ from maleo.dtos.resource import Resource
25
+ from ..response import (
26
+ ResponseT,
27
+ ErrorResponseT,
28
+ SuccessResponseT,
29
+ NoDataResponse,
30
+ CreateSingleDataResponse,
31
+ ReadSingleDataResponse,
32
+ UpdateSingleDataResponse,
33
+ DeleteSingleDataResponse,
34
+ CreateMultipleDataResponse,
35
+ ReadMultipleDataResponse,
36
+ UpdateMultipleDataResponse,
37
+ DeleteMultipleDataResponse,
38
+ )
39
+ from .base import BaseOperation
40
+
41
+
42
+ class ResourceOperationAction(BaseModel):
43
+ type: ResourceOperationType = Field(..., description="Resource operation's type")
44
+ create_type: Optional[ResourceOperationCreateType] = Field(
45
+ None, description="Resource operation's create type (optional)"
46
+ )
47
+ update_type: Optional[ResourceOperationUpdateType] = Field(
48
+ None, description="Resource operation's update type (optional)"
49
+ )
50
+ data_update_type: Optional[ResourceOperationDataUpdateType] = Field(
51
+ None, description="Resource operation's data update type (optional)"
52
+ )
53
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = Field(
54
+ None, description="Resource operation's status update type (optional)"
55
+ )
56
+
57
+
58
+ class CreateResourceOperationAction(ResourceOperationAction):
59
+ type: ResourceOperationType = ResourceOperationType.CREATE
60
+ update_type: Optional[ResourceOperationUpdateType] = None
61
+ data_update_type: Optional[ResourceOperationDataUpdateType] = None
62
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = None
63
+
64
+
65
+ class ReadResourceOperationAction(ResourceOperationAction):
66
+ type: ResourceOperationType = ResourceOperationType.READ
67
+ create_type: Optional[ResourceOperationCreateType] = None
68
+ update_type: Optional[ResourceOperationUpdateType] = None
69
+ data_update_type: Optional[ResourceOperationDataUpdateType] = None
70
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = None
71
+
72
+
73
+ class UpdateResourceOperationAction(ResourceOperationAction):
74
+ type: ResourceOperationType = ResourceOperationType.UPDATE
75
+ create_type: Optional[ResourceOperationCreateType] = None
76
+
77
+
78
+ class DeleteResourceOperationAction(ResourceOperationAction):
79
+ type: ResourceOperationType = ResourceOperationType.DELETE
80
+ create_type: Optional[ResourceOperationCreateType] = None
81
+ update_type: Optional[ResourceOperationUpdateType] = None
82
+ data_update_type: Optional[ResourceOperationDataUpdateType] = None
83
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = None
84
+
85
+
86
+ AllResourceOperationAction = Union[
87
+ CreateResourceOperationAction,
88
+ ReadResourceOperationAction,
89
+ UpdateResourceOperationAction,
90
+ DeleteResourceOperationAction,
91
+ ]
92
+
93
+
94
+ def extract_resource_operation_action(
95
+ request: Request, from_state: bool = True
96
+ ) -> AllResourceOperationAction:
97
+ if from_state:
98
+ operation_action = request.state.resource_operation_action
99
+
100
+ if not isinstance(
101
+ operation_action,
102
+ (
103
+ CreateResourceOperationAction,
104
+ ReadResourceOperationAction,
105
+ UpdateResourceOperationAction,
106
+ DeleteResourceOperationAction,
107
+ ),
108
+ ):
109
+ raise TypeError(
110
+ f"Invalid type of 'resource_operation_action': '{type(operation_action)}'"
111
+ )
112
+
113
+ return operation_action
114
+
115
+ else:
116
+ create_type = None
117
+ update_type = None
118
+ data_update_type = None
119
+ status_update_type = None
120
+
121
+ if request.method == "POST":
122
+ if request.url.path.endswith("/restore"):
123
+ create_type = ResourceOperationCreateType.RESTORE
124
+ else:
125
+ create_type = ResourceOperationCreateType.NEW
126
+ return CreateResourceOperationAction(create_type=create_type)
127
+ elif request.method == "GET":
128
+ return ReadResourceOperationAction()
129
+ elif request.method in ["PATCH", "PUT"]:
130
+ if request.method == "PUT":
131
+ update_type = ResourceOperationUpdateType.DATA
132
+ data_update_type = ResourceOperationDataUpdateType.FULL
133
+ elif request.method == "PATCH":
134
+ if request.url.path.endswith("/status"):
135
+ update_type = ResourceOperationUpdateType.STATUS
136
+ if request.query_params is not None:
137
+ match = re.search(
138
+ r"[?&]action=([^&]+)",
139
+ (
140
+ ""
141
+ if not request.query_params
142
+ else str(request.query_params)
143
+ ),
144
+ )
145
+ if match:
146
+ try:
147
+ status_update_type = ResourceOperationStatusUpdateType(
148
+ match.group(1)
149
+ )
150
+ except Exception:
151
+ pass
152
+ else:
153
+ update_type = ResourceOperationUpdateType.DATA
154
+ data_update_type = ResourceOperationDataUpdateType.PARTIAL
155
+ return UpdateResourceOperationAction(
156
+ update_type=update_type,
157
+ data_update_type=data_update_type,
158
+ status_update_type=status_update_type,
159
+ )
160
+ elif request.method == "DELETE":
161
+ return DeleteResourceOperationAction()
162
+ else:
163
+ raise ValueError("Unable to determine resource operation action")
164
+
165
+
166
+ def resource_operation_action_dependency(from_state: bool = True):
167
+
168
+ def dependency(request: Request) -> AllResourceOperationAction:
169
+ return extract_resource_operation_action(request, from_state=from_state)
170
+
171
+ return dependency
172
+
173
+
174
+ ResourceOperationActionT = TypeVar(
175
+ "ResourceOperationActionT", bound=ResourceOperationAction
176
+ )
177
+
178
+
179
+ class ResourceOperationActionMixin(BaseModel, Generic[ResourceOperationActionT]):
180
+ action: ResourceOperationActionT = Field(..., description="Operation's action.")
181
+
182
+
183
+ @overload
184
+ def generate_resource_operation_action(
185
+ *,
186
+ type: Literal[ResourceOperationType.CREATE],
187
+ create_type: Optional[ResourceOperationCreateType] = ...,
188
+ ) -> CreateResourceOperationAction: ...
189
+ @overload
190
+ def generate_resource_operation_action(
191
+ *,
192
+ type: Literal[ResourceOperationType.READ],
193
+ ) -> ReadResourceOperationAction: ...
194
+ @overload
195
+ def generate_resource_operation_action(
196
+ *,
197
+ type: Literal[ResourceOperationType.UPDATE],
198
+ update_type: Optional[ResourceOperationUpdateType] = ...,
199
+ data_update_type: Optional[ResourceOperationDataUpdateType] = ...,
200
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = ...,
201
+ ) -> UpdateResourceOperationAction: ...
202
+ @overload
203
+ def generate_resource_operation_action(
204
+ *,
205
+ type: Literal[ResourceOperationType.DELETE],
206
+ ) -> DeleteResourceOperationAction: ...
207
+ def generate_resource_operation_action(
208
+ *,
209
+ type: ResourceOperationType,
210
+ create_type: Optional[ResourceOperationCreateType] = None,
211
+ update_type: Optional[ResourceOperationUpdateType] = None,
212
+ data_update_type: Optional[ResourceOperationDataUpdateType] = None,
213
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = None,
214
+ ) -> AllResourceOperationAction:
215
+ if not isinstance(type, ResourceOperationType):
216
+ raise ValueError(f"Unsupported `type`: {type}")
217
+
218
+ if type is ResourceOperationType.CREATE:
219
+ return CreateResourceOperationAction(create_type=create_type)
220
+
221
+ elif type is ResourceOperationType.READ:
222
+ return ReadResourceOperationAction()
223
+
224
+ elif type is ResourceOperationType.UPDATE:
225
+ return UpdateResourceOperationAction(
226
+ update_type=update_type,
227
+ data_update_type=data_update_type,
228
+ status_update_type=status_update_type,
229
+ )
230
+
231
+ elif type is ResourceOperationType.DELETE:
232
+ return DeleteResourceOperationAction()
233
+
234
+
235
+ def resource_operation_action_from_request(
236
+ request: Request, from_state: bool = True
237
+ ) -> AllResourceOperationAction:
238
+ if from_state:
239
+ operation_action = request.state.operation_action
240
+
241
+ if operation_action is None:
242
+ raise ValueError(
243
+ "Can not retrieve 'operation_action' from the current request state"
244
+ )
245
+
246
+ if not isinstance(
247
+ operation_action,
248
+ (
249
+ CreateResourceOperationAction,
250
+ ReadResourceOperationAction,
251
+ UpdateResourceOperationAction,
252
+ DeleteResourceOperationAction,
253
+ ),
254
+ ):
255
+ raise ValueError(
256
+ f"Invalid 'operation_action' type: '{type(operation_action)}'"
257
+ )
258
+
259
+ return operation_action
260
+
261
+ if request.method == "POST":
262
+ if request.url.path.endswith("/restore"):
263
+ return generate_resource_operation_action(
264
+ type=ResourceOperationType.CREATE,
265
+ create_type=ResourceOperationCreateType.RESTORE,
266
+ )
267
+ else:
268
+ return generate_resource_operation_action(
269
+ type=ResourceOperationType.CREATE,
270
+ create_type=ResourceOperationCreateType.NEW,
271
+ )
272
+
273
+ elif request.method == "GET":
274
+ return generate_resource_operation_action(
275
+ type=ResourceOperationType.READ,
276
+ )
277
+
278
+ elif request.method in ["PATCH", "PUT"]:
279
+ if request.method == "PUT":
280
+ return generate_resource_operation_action(
281
+ type=ResourceOperationType.UPDATE,
282
+ update_type=ResourceOperationUpdateType.DATA,
283
+ )
284
+ elif request.method == "PATCH":
285
+ if not request.url.path.endswith("/status"):
286
+ return generate_resource_operation_action(
287
+ type=ResourceOperationType.UPDATE,
288
+ update_type=ResourceOperationUpdateType.DATA,
289
+ )
290
+ else:
291
+ if request.query_params is not None:
292
+ match = re.search(
293
+ r"[?&]action=([^&]+)",
294
+ ("" if not request.query_params else str(request.query_params)),
295
+ )
296
+ if match:
297
+ try:
298
+ return generate_resource_operation_action(
299
+ type=ResourceOperationType.UPDATE,
300
+ update_type=ResourceOperationUpdateType.STATUS,
301
+ status_update_type=ResourceOperationStatusUpdateType(
302
+ match.group(1)
303
+ ),
304
+ )
305
+ except Exception:
306
+ return generate_resource_operation_action(
307
+ type=ResourceOperationType.UPDATE,
308
+ update_type=ResourceOperationUpdateType.STATUS,
309
+ status_update_type=None,
310
+ )
311
+
312
+ elif request.method == "DELETE":
313
+ return generate_resource_operation_action(
314
+ type=ResourceOperationType.DELETE,
315
+ )
316
+
317
+ raise ValueError("Unable to map request's 'method' to 'operation_type'")
318
+
319
+
320
+ class ResourceOperation(
321
+ BaseOperation[
322
+ Resource,
323
+ SuccessT,
324
+ GenericErrorT,
325
+ Optional[RequestContext],
326
+ AuthenticationT,
327
+ ResourceOperationActionT,
328
+ None,
329
+ ResponseT,
330
+ ],
331
+ Generic[
332
+ SuccessT,
333
+ GenericErrorT,
334
+ AuthenticationT,
335
+ ResourceOperationActionT,
336
+ ResponseT,
337
+ ],
338
+ ):
339
+ type: OperationType = OperationType.RESOURCE
340
+ response_context: None = None
341
+
342
+
343
+ class FailedResourceOperation(
344
+ ResourceOperation[
345
+ Literal[False],
346
+ ErrorT,
347
+ AuthenticationT,
348
+ ResourceOperationActionT,
349
+ ErrorResponseT,
350
+ ],
351
+ Generic[ErrorT, AuthenticationT, ResourceOperationActionT, ErrorResponseT],
352
+ ):
353
+ success: Literal[False] = False
354
+
355
+
356
+ class CreateFailedResourceOperation(
357
+ FailedResourceOperation[
358
+ ErrorT,
359
+ AuthenticationT,
360
+ CreateResourceOperationAction,
361
+ ErrorResponseT,
362
+ ],
363
+ Generic[ErrorT, AuthenticationT, ErrorResponseT],
364
+ ):
365
+ pass
366
+
367
+
368
+ class ReadFailedResourceOperation(
369
+ FailedResourceOperation[
370
+ ErrorT, AuthenticationT, ReadResourceOperationAction, ErrorResponseT
371
+ ],
372
+ Generic[ErrorT, AuthenticationT, ErrorResponseT],
373
+ ):
374
+ pass
375
+
376
+
377
+ class UpdateFailedResourceOperation(
378
+ FailedResourceOperation[
379
+ ErrorT,
380
+ AuthenticationT,
381
+ UpdateResourceOperationAction,
382
+ ErrorResponseT,
383
+ ],
384
+ Generic[ErrorT, AuthenticationT, ErrorResponseT],
385
+ ):
386
+ pass
387
+
388
+
389
+ class DeleteFailedResourceOperation(
390
+ FailedResourceOperation[
391
+ ErrorT,
392
+ AuthenticationT,
393
+ DeleteResourceOperationAction,
394
+ ErrorResponseT,
395
+ ],
396
+ Generic[ErrorT, AuthenticationT, ErrorResponseT],
397
+ ):
398
+ pass
399
+
400
+
401
+ @overload
402
+ def generate_failed_resource_operation(
403
+ action: CreateResourceOperationAction,
404
+ *,
405
+ service_context: ServiceContext,
406
+ id: UUID,
407
+ context: OperationContext,
408
+ timestamp: OperationTimestamp,
409
+ summary: str,
410
+ error: ErrorT,
411
+ request_context: Optional[RequestContext],
412
+ authentication: AuthenticationT,
413
+ resource: Resource,
414
+ response: ErrorResponseT,
415
+ ) -> CreateFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
416
+ @overload
417
+ def generate_failed_resource_operation(
418
+ action: ReadResourceOperationAction,
419
+ *,
420
+ service_context: ServiceContext,
421
+ id: UUID,
422
+ context: OperationContext,
423
+ timestamp: OperationTimestamp,
424
+ summary: str,
425
+ error: ErrorT,
426
+ request_context: Optional[RequestContext],
427
+ authentication: AuthenticationT,
428
+ resource: Resource,
429
+ response: ErrorResponseT,
430
+ ) -> ReadFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
431
+ @overload
432
+ def generate_failed_resource_operation(
433
+ action: UpdateResourceOperationAction,
434
+ *,
435
+ service_context: ServiceContext,
436
+ id: UUID,
437
+ context: OperationContext,
438
+ timestamp: OperationTimestamp,
439
+ summary: str,
440
+ error: ErrorT,
441
+ request_context: Optional[RequestContext],
442
+ authentication: AuthenticationT,
443
+ resource: Resource,
444
+ response: ErrorResponseT,
445
+ ) -> UpdateFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
446
+ @overload
447
+ def generate_failed_resource_operation(
448
+ action: DeleteResourceOperationAction,
449
+ *,
450
+ service_context: ServiceContext,
451
+ id: UUID,
452
+ context: OperationContext,
453
+ timestamp: OperationTimestamp,
454
+ summary: str,
455
+ error: ErrorT,
456
+ request_context: Optional[RequestContext],
457
+ authentication: AuthenticationT,
458
+ resource: Resource,
459
+ response: ErrorResponseT,
460
+ ) -> DeleteFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
461
+ @overload
462
+ def generate_failed_resource_operation(
463
+ *,
464
+ type: Literal[ResourceOperationType.CREATE],
465
+ create_type: Optional[ResourceOperationCreateType] = ...,
466
+ service_context: ServiceContext,
467
+ id: UUID,
468
+ context: OperationContext,
469
+ timestamp: OperationTimestamp,
470
+ summary: str,
471
+ error: ErrorT,
472
+ request_context: Optional[RequestContext],
473
+ authentication: AuthenticationT,
474
+ resource: Resource,
475
+ response: ErrorResponseT,
476
+ ) -> CreateFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
477
+ @overload
478
+ def generate_failed_resource_operation(
479
+ *,
480
+ type: Literal[ResourceOperationType.READ],
481
+ service_context: ServiceContext,
482
+ id: UUID,
483
+ context: OperationContext,
484
+ timestamp: OperationTimestamp,
485
+ summary: str,
486
+ error: ErrorT,
487
+ request_context: Optional[RequestContext],
488
+ authentication: AuthenticationT,
489
+ resource: Resource,
490
+ response: ErrorResponseT,
491
+ ) -> ReadFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
492
+ @overload
493
+ def generate_failed_resource_operation(
494
+ *,
495
+ type: Literal[ResourceOperationType.UPDATE],
496
+ update_type: Optional[ResourceOperationUpdateType] = ...,
497
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = ...,
498
+ service_context: ServiceContext,
499
+ id: UUID,
500
+ context: OperationContext,
501
+ timestamp: OperationTimestamp,
502
+ summary: str,
503
+ error: ErrorT,
504
+ request_context: Optional[RequestContext],
505
+ authentication: AuthenticationT,
506
+ resource: Resource,
507
+ response: ErrorResponseT,
508
+ ) -> UpdateFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
509
+ @overload
510
+ def generate_failed_resource_operation(
511
+ *,
512
+ type: Literal[ResourceOperationType.DELETE],
513
+ service_context: ServiceContext,
514
+ id: UUID,
515
+ context: OperationContext,
516
+ timestamp: OperationTimestamp,
517
+ summary: str,
518
+ error: ErrorT,
519
+ request_context: Optional[RequestContext],
520
+ authentication: AuthenticationT,
521
+ resource: Resource,
522
+ response: ErrorResponseT,
523
+ ) -> DeleteFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT]: ...
524
+ def generate_failed_resource_operation(
525
+ action: Optional[AllResourceOperationAction] = None,
526
+ *,
527
+ type: Optional[ResourceOperationType] = None,
528
+ create_type: Optional[ResourceOperationCreateType] = None,
529
+ update_type: Optional[ResourceOperationUpdateType] = None,
530
+ status_update_type: Optional[ResourceOperationStatusUpdateType] = None,
531
+ service_context: ServiceContext,
532
+ id: UUID,
533
+ context: OperationContext,
534
+ timestamp: OperationTimestamp,
535
+ summary: str,
536
+ error: ErrorT,
537
+ request_context: Optional[RequestContext],
538
+ authentication: AuthenticationT,
539
+ resource: Resource,
540
+ response: ErrorResponseT,
541
+ ) -> Union[
542
+ CreateFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT],
543
+ ReadFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT],
544
+ UpdateFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT],
545
+ DeleteFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT],
546
+ ]:
547
+ if (action is None and type is None) or (action is not None and type is not None):
548
+ raise ValueError("Only either 'action' or 'type' must be given")
549
+
550
+ if action is not None:
551
+ if not isinstance(
552
+ action,
553
+ (
554
+ CreateResourceOperationAction,
555
+ ReadResourceOperationAction,
556
+ UpdateResourceOperationAction,
557
+ DeleteResourceOperationAction,
558
+ ),
559
+ ):
560
+ raise ValueError(f"Invalid 'action' type: '{type(action)}'")
561
+
562
+ if isinstance(action, CreateResourceOperationAction):
563
+ return CreateFailedResourceOperation[
564
+ ErrorT, AuthenticationT, ErrorResponseT
565
+ ](
566
+ service_context=service_context,
567
+ id=id,
568
+ context=context,
569
+ timestamp=timestamp,
570
+ summary=summary,
571
+ resource=resource,
572
+ error=error,
573
+ request_context=request_context,
574
+ authentication=authentication,
575
+ action=action,
576
+ response=response,
577
+ )
578
+ elif isinstance(action, ReadResourceOperationAction):
579
+ return ReadFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT](
580
+ service_context=service_context,
581
+ id=id,
582
+ context=context,
583
+ timestamp=timestamp,
584
+ summary=summary,
585
+ resource=resource,
586
+ error=error,
587
+ request_context=request_context,
588
+ authentication=authentication,
589
+ action=action,
590
+ response=response,
591
+ )
592
+ elif isinstance(action, UpdateResourceOperationAction):
593
+ return UpdateFailedResourceOperation[
594
+ ErrorT, AuthenticationT, ErrorResponseT
595
+ ](
596
+ service_context=service_context,
597
+ id=id,
598
+ context=context,
599
+ timestamp=timestamp,
600
+ summary=summary,
601
+ resource=resource,
602
+ error=error,
603
+ request_context=request_context,
604
+ authentication=authentication,
605
+ action=action,
606
+ response=response,
607
+ )
608
+ elif isinstance(action, DeleteResourceOperationAction):
609
+ return DeleteFailedResourceOperation[
610
+ ErrorT, AuthenticationT, ErrorResponseT
611
+ ](
612
+ service_context=service_context,
613
+ id=id,
614
+ context=context,
615
+ timestamp=timestamp,
616
+ summary=summary,
617
+ resource=resource,
618
+ error=error,
619
+ request_context=request_context,
620
+ authentication=authentication,
621
+ action=action,
622
+ response=response,
623
+ )
624
+
625
+ elif type is not None:
626
+ if not isinstance(type, ResourceOperationType):
627
+ raise ValueError(f"Unsupported `type`: {type}")
628
+
629
+ if type is ResourceOperationType.CREATE:
630
+ action = generate_resource_operation_action(
631
+ type=type, create_type=create_type
632
+ )
633
+ return CreateFailedResourceOperation[
634
+ ErrorT, AuthenticationT, ErrorResponseT
635
+ ](
636
+ service_context=service_context,
637
+ id=id,
638
+ context=context,
639
+ timestamp=timestamp,
640
+ summary=summary,
641
+ resource=resource,
642
+ error=error,
643
+ request_context=request_context,
644
+ authentication=authentication,
645
+ action=action,
646
+ response=response,
647
+ )
648
+ elif type is ResourceOperationType.READ:
649
+ action = generate_resource_operation_action(type=type)
650
+ return ReadFailedResourceOperation[ErrorT, AuthenticationT, ErrorResponseT](
651
+ service_context=service_context,
652
+ id=id,
653
+ context=context,
654
+ timestamp=timestamp,
655
+ summary=summary,
656
+ resource=resource,
657
+ error=error,
658
+ request_context=request_context,
659
+ authentication=authentication,
660
+ action=action,
661
+ response=response,
662
+ )
663
+ elif type is ResourceOperationType.UPDATE:
664
+ action = generate_resource_operation_action(
665
+ type=type,
666
+ update_type=update_type,
667
+ status_update_type=status_update_type,
668
+ )
669
+ return UpdateFailedResourceOperation[
670
+ ErrorT, AuthenticationT, ErrorResponseT
671
+ ](
672
+ service_context=service_context,
673
+ id=id,
674
+ context=context,
675
+ timestamp=timestamp,
676
+ summary=summary,
677
+ resource=resource,
678
+ error=error,
679
+ request_context=request_context,
680
+ authentication=authentication,
681
+ action=action,
682
+ response=response,
683
+ )
684
+ elif type is ResourceOperationType.DELETE:
685
+ action = generate_resource_operation_action(type=type)
686
+ return DeleteFailedResourceOperation[
687
+ ErrorT, AuthenticationT, ErrorResponseT
688
+ ](
689
+ service_context=service_context,
690
+ id=id,
691
+ context=context,
692
+ timestamp=timestamp,
693
+ summary=summary,
694
+ resource=resource,
695
+ error=error,
696
+ request_context=request_context,
697
+ authentication=authentication,
698
+ action=action,
699
+ response=response,
700
+ )
701
+ else:
702
+ # This should never happen due to initial validation,
703
+ # but type checker needs to see all paths covered
704
+ raise ValueError("Neither 'action' nor 'type' provided")
705
+
706
+
707
+ class SuccessfulResourceOperation(
708
+ ResourceOperation[
709
+ Literal[True],
710
+ None,
711
+ AuthenticationT,
712
+ ResourceOperationActionT,
713
+ SuccessResponseT,
714
+ ],
715
+ Generic[AuthenticationT, ResourceOperationActionT, SuccessResponseT],
716
+ ):
717
+ success: Literal[True] = True
718
+ error: None = None
719
+
720
+
721
+ class NoDataResourceOperation(
722
+ SuccessfulResourceOperation[
723
+ AuthenticationT,
724
+ ResourceOperationActionT,
725
+ NoDataResponse[MetadataT],
726
+ ],
727
+ Generic[ResourceOperationActionT, AuthenticationT, MetadataT],
728
+ ):
729
+ pass
730
+
731
+
732
+ class CreateSingleResourceOperation(
733
+ SuccessfulResourceOperation[
734
+ AuthenticationT,
735
+ CreateResourceOperationAction,
736
+ CreateSingleDataResponse[DataT, MetadataT],
737
+ ],
738
+ Generic[
739
+ AuthenticationT,
740
+ DataT,
741
+ MetadataT,
742
+ ],
743
+ ):
744
+ pass
745
+
746
+
747
+ class ReadSingleResourceOperation(
748
+ SuccessfulResourceOperation[
749
+ AuthenticationT,
750
+ ReadResourceOperationAction,
751
+ ReadSingleDataResponse[DataT, MetadataT],
752
+ ],
753
+ Generic[
754
+ AuthenticationT,
755
+ DataT,
756
+ MetadataT,
757
+ ],
758
+ ):
759
+ pass
760
+
761
+
762
+ class UpdateSingleResourceOperation(
763
+ SuccessfulResourceOperation[
764
+ AuthenticationT,
765
+ UpdateResourceOperationAction,
766
+ UpdateSingleDataResponse[DataT, MetadataT],
767
+ ],
768
+ Generic[
769
+ AuthenticationT,
770
+ DataT,
771
+ MetadataT,
772
+ ],
773
+ ):
774
+ pass
775
+
776
+
777
+ class DeleteSingleResourceOperation(
778
+ SuccessfulResourceOperation[
779
+ AuthenticationT,
780
+ DeleteResourceOperationAction,
781
+ DeleteSingleDataResponse[DataT, MetadataT],
782
+ ],
783
+ Generic[
784
+ AuthenticationT,
785
+ DataT,
786
+ MetadataT,
787
+ ],
788
+ ):
789
+ pass
790
+
791
+
792
+ class CreateMultipleResourceOperation(
793
+ SuccessfulResourceOperation[
794
+ AuthenticationT,
795
+ CreateResourceOperationAction,
796
+ CreateMultipleDataResponse[DataT, PaginationT, MetadataT],
797
+ ],
798
+ Generic[
799
+ AuthenticationT,
800
+ DataT,
801
+ PaginationT,
802
+ MetadataT,
803
+ ],
804
+ ):
805
+ pass
806
+
807
+
808
+ class ReadMultipleResourceOperation(
809
+ SuccessfulResourceOperation[
810
+ AuthenticationT,
811
+ ReadResourceOperationAction,
812
+ ReadMultipleDataResponse[DataT, PaginationT, MetadataT],
813
+ ],
814
+ Generic[
815
+ AuthenticationT,
816
+ DataT,
817
+ PaginationT,
818
+ MetadataT,
819
+ ],
820
+ ):
821
+ pass
822
+
823
+
824
+ class UpdateMultipleResourceOperation(
825
+ SuccessfulResourceOperation[
826
+ AuthenticationT,
827
+ UpdateResourceOperationAction,
828
+ UpdateMultipleDataResponse[DataT, PaginationT, MetadataT],
829
+ ],
830
+ Generic[
831
+ AuthenticationT,
832
+ DataT,
833
+ PaginationT,
834
+ MetadataT,
835
+ ],
836
+ ):
837
+ pass
838
+
839
+
840
+ class DeleteMultipleResourceOperation(
841
+ SuccessfulResourceOperation[
842
+ AuthenticationT,
843
+ DeleteResourceOperationAction,
844
+ DeleteMultipleDataResponse[DataT, PaginationT, MetadataT],
845
+ ],
846
+ Generic[
847
+ AuthenticationT,
848
+ DataT,
849
+ PaginationT,
850
+ MetadataT,
851
+ ],
852
+ ):
853
+ pass