Skip to content

Commit bbe1f9d

Browse files
committed
create context var for current atomic operation
1 parent 96434d0 commit bbe1f9d

File tree

3 files changed

+33
-11
lines changed

3 files changed

+33
-11
lines changed

fastapi_jsonapi/atomic/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1-
__all__ = ("AtomicOperations",)
1+
__all__ = (
2+
"AtomicOperations",
3+
"current_atomic_operation",
4+
)
25

36
from .atomic import AtomicOperations
7+
from .atomic_handler import current_atomic_operation

fastapi_jsonapi/atomic/atomic_handler.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import logging
44
from collections import defaultdict
5+
from contextvars import ContextVar
56
from functools import wraps
67
from typing import (
78
TYPE_CHECKING,
@@ -28,6 +29,8 @@
2829
log = logging.getLogger(__name__)
2930
AtomicResponseDict = TypedDict("AtomicResponseDict", {"atomic:results": List[Any]})
3031

32+
current_atomic_operation: ContextVar[OperationBase] = ContextVar("current_atomic_operation")
33+
3134

3235
def catch_exc_on_operation_handle(func: Callable[..., Awaitable]):
3336
@wraps(func)
@@ -82,7 +85,7 @@ async def prepare_one_operation(self, operation: AtomicOperation):
8285
raise ValueError(msg)
8386
jsonapi = RoutersJSONAPI.all_jsonapi_routers[operation_type]
8487

85-
one_operation = await OperationBase.prepare(
88+
one_operation = OperationBase.prepare(
8689
action=operation.op,
8790
request=self.request,
8891
jsonapi=jsonapi,
@@ -116,6 +119,9 @@ async def handle(self) -> Union[AtomicResponseDict, AtomicResultResponse, None]:
116119
success = True
117120
previous_dl: Optional[BaseDataLayer] = None
118121
for operation in prepared_operations:
122+
# set context var
123+
ctx_var_token = current_atomic_operation.set(operation)
124+
119125
dl: BaseDataLayer = await operation.get_data_layer()
120126
await dl.atomic_start(previous_dl=previous_dl)
121127
response = await self.process_one_operation(
@@ -136,6 +142,9 @@ async def handle(self) -> Union[AtomicResponseDict, AtomicResultResponse, None]:
136142
if operation.data.lid and response.data:
137143
self.local_ids_cache[operation.data.type][operation.data.lid] = response.data.id
138144

145+
# reset context var
146+
current_atomic_operation.reset(ctx_var_token)
147+
139148
if previous_dl:
140149
await previous_dl.atomic_end(success=success)
141150

fastapi_jsonapi/atomic/prepared_atomic_operation.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ class OperationBase:
2424
view: ViewBase
2525
ref: Optional[AtomicOperationRef]
2626
data: OperationDataType
27-
data_layer_view_dependencies: Dict[str, Any]
2827
op_type: str
2928

29+
@property
30+
def http_method(self) -> HTTPMethod:
31+
raise NotImplementedError
32+
3033
@classmethod
31-
async def prepare(
34+
def prepare(
3235
cls,
3336
action: str,
3437
request: Request,
@@ -55,22 +58,21 @@ async def prepare(
5558

5659
view = view_cls(request=request, jsonapi=jsonapi)
5760

58-
data_layer_view_dependencies: Dict[str, Any] = await jsonapi.handle_view_dependencies(
59-
request=request,
60-
view_cls=view_cls,
61-
method=operation_cls.http_method,
62-
)
6361
return operation_cls(
6462
jsonapi=jsonapi,
6563
view=view,
6664
ref=ref,
6765
data=data,
68-
data_layer_view_dependencies=data_layer_view_dependencies,
6966
op_type=action,
7067
)
7168

7269
async def get_data_layer(self) -> BaseDataLayer:
73-
return await self.view.get_data_layer(self.data_layer_view_dependencies)
70+
data_layer_view_dependencies: Dict[str, Any] = await self.jsonapi.handle_view_dependencies(
71+
request=self.view.request,
72+
view_cls=self.view.__class__,
73+
method=self.http_method,
74+
)
75+
return await self.view.get_data_layer(data_layer_view_dependencies)
7476

7577
async def handle(self, dl: BaseDataLayer):
7678
raise NotImplementedError
@@ -135,6 +137,9 @@ class OperationAdd(ListOperationBase):
135137
http_method = HTTPMethod.POST
136138

137139
async def handle(self, dl: BaseDataLayer):
140+
# use outer schema wrapper because we need this error path:
141+
# `{'loc': ['data', 'attributes', 'name']`
142+
# and not `{'loc': ['attributes', 'name']`
138143
data_in = self.jsonapi.schema_in_post(data=self.data)
139144
response = await self.view.process_create_object(
140145
dl=dl,
@@ -151,6 +156,10 @@ async def handle(self, dl: BaseDataLayer):
151156
# TODO: clear to-one relationships
152157
pass
153158
# TODO: handle relationship update requests (relationship resources)
159+
160+
# use outer schema wrapper because we need this error path:
161+
# `{'loc': ['data', 'attributes', 'name']`
162+
# and not `{'loc': ['attributes', 'name']`
154163
data_in = self.jsonapi.schema_in_patch(data=self.data)
155164
obj_id = self.ref and self.ref.id or self.data and self.data.id
156165
response = await self.view.process_update_object(

0 commit comments

Comments
 (0)