aws-lambda-powertools 3.10.1a11__py3-none-any.whl → 3.11.1a0__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 (20) hide show
  1. aws_lambda_powertools/event_handler/__init__.py +2 -0
  2. aws_lambda_powertools/event_handler/api_gateway.py +6 -20
  3. aws_lambda_powertools/event_handler/appsync.py +4 -29
  4. aws_lambda_powertools/event_handler/events_appsync/__init__.py +5 -0
  5. aws_lambda_powertools/event_handler/events_appsync/_registry.py +92 -0
  6. aws_lambda_powertools/event_handler/events_appsync/appsync_events.py +422 -0
  7. aws_lambda_powertools/event_handler/events_appsync/base.py +44 -0
  8. aws_lambda_powertools/event_handler/events_appsync/exceptions.py +25 -0
  9. aws_lambda_powertools/event_handler/events_appsync/functions.py +106 -0
  10. aws_lambda_powertools/event_handler/events_appsync/router.py +199 -0
  11. aws_lambda_powertools/event_handler/events_appsync/types.py +21 -0
  12. aws_lambda_powertools/event_handler/exception_handling.py +118 -0
  13. aws_lambda_powertools/shared/version.py +1 -1
  14. aws_lambda_powertools/utilities/data_classes/__init__.py +2 -0
  15. aws_lambda_powertools/utilities/data_classes/appsync_resolver_event.py +41 -34
  16. aws_lambda_powertools/utilities/data_classes/appsync_resolver_events_event.py +56 -0
  17. {aws_lambda_powertools-3.10.1a11.dist-info → aws_lambda_powertools-3.11.1a0.dist-info}/METADATA +1 -1
  18. {aws_lambda_powertools-3.10.1a11.dist-info → aws_lambda_powertools-3.11.1a0.dist-info}/RECORD +20 -10
  19. {aws_lambda_powertools-3.10.1a11.dist-info → aws_lambda_powertools-3.11.1a0.dist-info}/LICENSE +0 -0
  20. {aws_lambda_powertools-3.10.1a11.dist-info → aws_lambda_powertools-3.11.1a0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Callable
5
+
6
+ DEFAULT_ROUTE = "/default/*"
7
+
8
+
9
+ class BaseRouter(ABC):
10
+ """Abstract base class for Router (resolvers)"""
11
+
12
+ @abstractmethod
13
+ def on_publish(
14
+ self,
15
+ path: str = DEFAULT_ROUTE,
16
+ aggregate: bool = True,
17
+ ) -> Callable:
18
+ raise NotImplementedError
19
+
20
+ @abstractmethod
21
+ def async_on_publish(
22
+ self,
23
+ path: str = DEFAULT_ROUTE,
24
+ aggregate: bool = True,
25
+ ) -> Callable:
26
+ raise NotImplementedError
27
+
28
+ @abstractmethod
29
+ def on_subscribe(
30
+ self,
31
+ path: str = DEFAULT_ROUTE,
32
+ ) -> Callable:
33
+ raise NotImplementedError
34
+
35
+ def append_context(self, **additional_context) -> None:
36
+ """
37
+ Appends context information available under any route.
38
+
39
+ Parameters
40
+ -----------
41
+ **additional_context: dict
42
+ Additional context key-value pairs to append.
43
+ """
44
+ raise NotImplementedError
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class UnauthorizedException(Exception):
5
+ """
6
+ Error to be thrown to communicate the subscription is unauthorized.
7
+
8
+ When this error is raised, the client will receive a 40x error code
9
+ and the subscription will be closed.
10
+
11
+ Attributes:
12
+ message (str): The error message describing the unauthorized access.
13
+ """
14
+
15
+ def __init__(self, message: str | None = None, *args, **kwargs):
16
+ """
17
+ Initialize the UnauthorizedException.
18
+
19
+ Args:
20
+ message (str): A descriptive error message.
21
+ *args: Variable positional arguments.
22
+ **kwargs: Variable keyword arguments.
23
+ """
24
+ super().__init__(message, *args, **kwargs)
25
+ self.name = "UnauthorizedException"
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from functools import lru_cache
5
+ from typing import Any
6
+
7
+ PATH_REGEX = re.compile(r"^\/([^\/\*]+)(\/[^\/\*]+)*(\/\*)?$")
8
+
9
+
10
+ def is_valid_path(path: str) -> bool:
11
+ """
12
+ Checks if a given path is valid based on specific rules.
13
+
14
+ Parameters
15
+ ----------
16
+ path: str
17
+ The path to validate
18
+
19
+ Returns:
20
+ --------
21
+ bool:
22
+ True if the path is valid, False otherwise
23
+
24
+ Examples:
25
+ >>> is_valid_path('/*')
26
+ True
27
+ >>> is_valid_path('/users')
28
+ True
29
+ >>> is_valid_path('/users/profile')
30
+ True
31
+ >>> is_valid_path('/users/*/details')
32
+ False
33
+ >>> is_valid_path('/users/*')
34
+ True
35
+ >>> is_valid_path('users')
36
+ False
37
+ """
38
+ return True if path == "/*" else bool(PATH_REGEX.fullmatch(path))
39
+
40
+
41
+ def find_best_route(routes: dict[str, Any], path: str):
42
+ """
43
+ Find the most specific matching route for a given path.
44
+
45
+ Examples of matches:
46
+ Route: /default/v1/* Path: /default/v1/users -> MATCH
47
+ Route: /default/v1/* Path: /default/v1/users/students -> MATCH
48
+ Route: /default/v1/users/* Path: /default/v1/users/123 -> MATCH (this wins over /default/v1/*)
49
+ Route: /* Path: /anything/here -> MATCH (lowest priority)
50
+
51
+ Parameters
52
+ ----------
53
+ routes: dict[str, Any]
54
+ Dictionary containing routes and their handlers
55
+ Format: {
56
+ 'resolvers': {
57
+ '/path/*': {'func': callable, 'aggregate': bool},
58
+ '/path/specific/*': {'func': callable, 'aggregate': bool}
59
+ }
60
+ }
61
+ path: str
62
+ Actual path to match (e.g., '/default/v1/users')
63
+
64
+ Returns
65
+ -------
66
+ str: Most specific matching route or None if no match
67
+ """
68
+
69
+ @lru_cache(maxsize=1024)
70
+ def pattern_to_regex(route):
71
+ """
72
+ Convert a route pattern to a regex pattern with caching.
73
+ Examples:
74
+ /default/v1/* -> ^/default/v1/[^/]+$
75
+ /default/v1/users/* -> ^/default/v1/users/.*$
76
+
77
+ Parameters
78
+ ----------
79
+ route: str
80
+ Route pattern with wildcards
81
+
82
+ Returns
83
+ -------
84
+ Pattern:
85
+ Compiled regex pattern
86
+ """
87
+ # Escape special regex chars but convert * to regex pattern
88
+ pattern = re.escape(route).replace("\\*", "[^/]+")
89
+
90
+ # If pattern ends with [^/]+, replace with .* for multi-segment match
91
+ if pattern.endswith("[^/]+"):
92
+ pattern = pattern[:-6] + ".*"
93
+
94
+ # Compile and return the regex pattern
95
+ return re.compile(f"^{pattern}$")
96
+
97
+ # Find all matching routes
98
+ matches = [route for route in routes.keys() if pattern_to_regex(route).match(path)]
99
+
100
+ # Return the most specific route (longest length minus wildcards)
101
+ # Examples of specificity:
102
+ # - '/default/v1/users' -> score: 14 (len=14, wildcards=0)
103
+ # - '/default/v1/users/*' -> score: 14 (len=15, wildcards=1)
104
+ # - '/default/v1/*' -> score: 8 (len=9, wildcards=1)
105
+ # - '/*' -> score: 0 (len=2, wildcards=1)
106
+ return max(matches, key=lambda x: len(x) - x.count("*"), default=None)
@@ -0,0 +1,199 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from aws_lambda_powertools.event_handler.events_appsync._registry import ResolverEventsRegistry
6
+ from aws_lambda_powertools.event_handler.events_appsync.base import DEFAULT_ROUTE, BaseRouter
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Callable
10
+
11
+ from aws_lambda_powertools.utilities.data_classes.appsync_resolver_events_event import AppSyncResolverEventsEvent
12
+ from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext
13
+
14
+
15
+ class Router(BaseRouter):
16
+ """
17
+ Router for AppSync real-time API event handling.
18
+
19
+ This class provides decorators to register resolver functions for publish and subscribe
20
+ operations in AppSync real-time APIs.
21
+
22
+ Parameters
23
+ ----------
24
+ context : dict
25
+ Dictionary to store context information accessible across resolvers
26
+ current_event : AppSyncResolverEventsEvent
27
+ Current event being processed
28
+ lambda_context : LambdaContext
29
+ Lambda context from the AWS Lambda function
30
+
31
+ Examples
32
+ --------
33
+ Create a router and define resolvers:
34
+
35
+ >>> chat_router = Router()
36
+ >>>
37
+ >>> # Register a resolver for publish operations
38
+ >>> @chat_router.on_publish(path="/chat/message")
39
+ >>> def handle_message(payload):
40
+ >>> # Process message
41
+ >>> return {"success": True, "messageId": payload.get("id")}
42
+ >>>
43
+ >>> # Register an async resolver for publish operations
44
+ >>> @chat_router.async_on_publish(path="/chat/typing")
45
+ >>> async def handle_typing(event):
46
+ >>> # Process typing indicator
47
+ >>> await some_async_operation()
48
+ >>> return {"processed": True}
49
+ >>>
50
+ >>> # Register a resolver for subscribe operations
51
+ >>> @chat_router.on_subscribe(path="/chat/room/*")
52
+ >>> def handle_subscribe(event):
53
+ >>> # Handle subscription setup
54
+ >>> return {"allowed": True}
55
+ """
56
+
57
+ context: dict
58
+ current_event: AppSyncResolverEventsEvent
59
+ lambda_context: LambdaContext
60
+
61
+ def __init__(self):
62
+ """
63
+ Initialize a new Router instance.
64
+
65
+ Sets up empty context and registry containers for different types of resolvers.
66
+ """
67
+ self.context = {} # early init as customers might add context before event resolution
68
+ self._publish_registry = ResolverEventsRegistry(kind_resolver="on_publish")
69
+ self._async_publish_registry = ResolverEventsRegistry(kind_resolver="async_on_publish")
70
+ self._subscribe_registry = ResolverEventsRegistry(kind_resolver="on_subscribe")
71
+
72
+ def on_publish(
73
+ self,
74
+ path: str = DEFAULT_ROUTE,
75
+ aggregate: bool = False,
76
+ ) -> Callable:
77
+ """
78
+ Register a resolver function for publish operations.
79
+
80
+ Parameters
81
+ ----------
82
+ path : str, optional
83
+ The channel path pattern to match for this resolver, by default "/default/*"
84
+ aggregate : bool, optional
85
+ Whether to process events in aggregate (batch) mode, by default False
86
+
87
+ Returns
88
+ -------
89
+ Callable
90
+ Decorator function that registers the resolver
91
+
92
+ Examples
93
+ --------
94
+ >>> router = Router()
95
+ >>>
96
+ >>> # Basic usage
97
+ >>> @router.on_publish(path="/notifications/new")
98
+ >>> def handle_notification(payload):
99
+ >>> # Process a single notification
100
+ >>> return {"processed": True, "notificationId": payload.get("id")}
101
+ >>>
102
+ >>> # Aggregate mode for batch processing
103
+ >>> @router.on_publish(path="/notifications/batch", aggregate=True)
104
+ >>> def handle_batch_notifications(payload):
105
+ >>> # Process multiple notifications at once
106
+ >>> results = []
107
+ >>> for item in payload:
108
+ >>> # Process each item
109
+ >>> results.append({"processed": True, "id": item.get("id")})
110
+ >>> return results
111
+ """
112
+ return self._publish_registry.register(path=path, aggregate=aggregate)
113
+
114
+ def async_on_publish(
115
+ self,
116
+ path: str = DEFAULT_ROUTE,
117
+ aggregate: bool = False,
118
+ ) -> Callable:
119
+ """
120
+ Register an asynchronous resolver function for publish operations.
121
+
122
+ Parameters
123
+ ----------
124
+ path : str, optional
125
+ The channel path pattern to match for this resolver, by default "/default/*"
126
+ aggregate : bool, optional
127
+ Whether to process events in aggregate (batch) mode, by default False
128
+
129
+ Returns
130
+ -------
131
+ Callable
132
+ Decorator function that registers the async resolver
133
+
134
+ Examples
135
+ --------
136
+ >>> router = Router()
137
+ >>>
138
+ >>> # Basic async usage
139
+ >>> @router.async_on_publish(path="/messages/send")
140
+ >>> async def handle_message(event):
141
+ >>> # Perform async operations
142
+ >>> result = await database.save_message(event)
143
+ >>> return {"saved": True, "messageId": result.id}
144
+ >>>
145
+ >>> # Aggregate mode for batch processing
146
+ >>> @router.async_on_publish(path="/messages/batch", aggregate=True)
147
+ >>> async def handle_batch_messages(events):
148
+ >>> # Process multiple messages asynchronously
149
+ >>> tasks = [database.save_message(e) for e in events]
150
+ >>> results = await asyncio.gather(*tasks)
151
+ >>> return [{"saved": True, "id": r.id} for r in results]
152
+ """
153
+ return self._async_publish_registry.register(path=path, aggregate=aggregate)
154
+
155
+ def on_subscribe(
156
+ self,
157
+ path: str = DEFAULT_ROUTE,
158
+ ) -> Callable:
159
+ """
160
+ Register a resolver function for subscribe operations.
161
+
162
+ Parameters
163
+ ----------
164
+ path : str, optional
165
+ The channel path pattern to match for this resolver, by default "/default/*"
166
+
167
+ Returns
168
+ -------
169
+ Callable
170
+ Decorator function that registers the resolver
171
+
172
+ Examples
173
+ --------
174
+ >>> router = Router()
175
+ >>>
176
+ >>> # Handle subscription request
177
+ >>> @router.on_subscribe(path="/chat/room/*")
178
+ >>> def authorize_subscription(event):
179
+ >>> # Verify if the client can subscribe to this room
180
+ >>> room_id = event.info.channel_path.split('/')[-1]
181
+ >>> user_id = event.identity.username
182
+ >>>
183
+ >>> # Check if user is allowed in this room
184
+ >>> is_allowed = check_permission(user_id, room_id)
185
+ >>>
186
+ >>> return {
187
+ >>> "allowed": is_allowed,
188
+ >>> "roomId": room_id
189
+ >>> }
190
+ """
191
+ return self._subscribe_registry.register(path=path)
192
+
193
+ def append_context(self, **additional_context):
194
+ """Append key=value data as routing context"""
195
+ self.context.update(**additional_context)
196
+
197
+ def clear_context(self):
198
+ """Resets routing context"""
199
+ self.context.clear()
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, TypedDict
4
+
5
+ if TYPE_CHECKING:
6
+ from collections.abc import Callable
7
+
8
+
9
+ class ResolverTypeDef(TypedDict):
10
+ """
11
+ Type definition for resolver dictionary
12
+ Parameters
13
+ ----------
14
+ func: Callable[..., Any]
15
+ Resolver function
16
+ aggregate: bool
17
+ Aggregation flag or method
18
+ """
19
+
20
+ func: Callable[..., Any]
21
+ aggregate: bool
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Mapping
4
+
5
+ if TYPE_CHECKING:
6
+ from collections.abc import Callable
7
+
8
+
9
+ class ExceptionHandlerManager:
10
+ """
11
+ A class to manage exception handlers for different exception types.
12
+ This class allows registering handler functions for specific exception types
13
+ and looking up the appropriate handler when an exception occurs.
14
+ Example usage:
15
+ -------------
16
+ handler_manager = ExceptionHandlerManager()
17
+ @handler_manager.exception_handler(ValueError)
18
+ def handle_value_error(e):
19
+ print(f"Handling ValueError: {e}")
20
+ return "Error handled"
21
+ # To handle multiple exception types with the same handler:
22
+ @handler_manager.exception_handler([KeyError, TypeError])
23
+ def handle_multiple_errors(e):
24
+ print(f"Handling {type(e).__name__}: {e}")
25
+ return "Multiple error types handled"
26
+ # To find and execute a handler:
27
+ try:
28
+ # some code that might raise an exception
29
+ raise ValueError("Invalid value")
30
+ except Exception as e:
31
+ handler = handler_manager.lookup_exception_handler(type(e))
32
+ if handler:
33
+ result = handler(e)
34
+ """
35
+
36
+ def __init__(self):
37
+ """Initialize an empty dictionary to store exception handlers."""
38
+ self._exception_handlers: dict[type[Exception], Callable] = {}
39
+
40
+ def exception_handler(self, exc_class: type[Exception] | list[type[Exception]]):
41
+ """
42
+ A decorator function that registers a handler for one or more exception types.
43
+ Parameters
44
+ ----------
45
+ exc_class : type[Exception] | list[type[Exception]]
46
+ A single exception type or a list of exception types.
47
+ Returns
48
+ -------
49
+ Callable
50
+ A decorator function that registers the exception handler.
51
+ """
52
+
53
+ def register_exception_handler(func: Callable):
54
+ if isinstance(exc_class, list):
55
+ for exp in exc_class:
56
+ self._exception_handlers[exp] = func
57
+ else:
58
+ self._exception_handlers[exc_class] = func
59
+ return func
60
+
61
+ return register_exception_handler
62
+
63
+ def lookup_exception_handler(self, exp_type: type) -> Callable | None:
64
+ """
65
+ Looks up the registered exception handler for the given exception type or its base classes.
66
+ Parameters
67
+ ----------
68
+ exp_type : type
69
+ The exception type to look up the handler for.
70
+ Returns
71
+ -------
72
+ Callable | None
73
+ The registered exception handler function if found, otherwise None.
74
+ """
75
+ for cls in exp_type.__mro__:
76
+ if cls in self._exception_handlers:
77
+ return self._exception_handlers[cls]
78
+ return None
79
+
80
+ def update_exception_handlers(self, handlers: Mapping[type[Exception], Callable]) -> None:
81
+ """
82
+ Updates the exception handlers dictionary with new handler mappings.
83
+ This method allows bulk updates of exception handlers by providing a dictionary
84
+ mapping exception types to handler functions.
85
+ Parameters
86
+ ----------
87
+ handlers : Mapping[Type[Exception], Callable]
88
+ A dictionary mapping exception types to handler functions.
89
+ Example
90
+ -------
91
+ >>> def handle_value_error(e):
92
+ ... print(f"Value error: {e}")
93
+ ...
94
+ >>> def handle_key_error(e):
95
+ ... print(f"Key error: {e}")
96
+ ...
97
+ >>> handler_manager.update_exception_handlers({
98
+ ... ValueError: handle_value_error,
99
+ ... KeyError: handle_key_error
100
+ ... })
101
+ """
102
+ self._exception_handlers.update(handlers)
103
+
104
+ def get_registered_handlers(self) -> dict[type[Exception], Callable]:
105
+ """
106
+ Returns all registered exception handlers.
107
+ Returns
108
+ -------
109
+ Dict[Type[Exception], Callable]
110
+ A dictionary mapping exception types to their handler functions.
111
+ """
112
+ return self._exception_handlers.copy()
113
+
114
+ def clear_handlers(self) -> None:
115
+ """
116
+ Clears all registered exception handlers.
117
+ """
118
+ self._exception_handlers.clear()
@@ -1,3 +1,3 @@
1
1
  """Exposes version constant to avoid circular dependencies."""
2
2
 
3
- VERSION = "3.10.1a11"
3
+ VERSION = "3.11.1a0"
@@ -6,6 +6,7 @@ from .alb_event import ALBEvent
6
6
  from .api_gateway_proxy_event import APIGatewayProxyEvent, APIGatewayProxyEventV2
7
7
  from .api_gateway_websocket_event import APIGatewayWebSocketEvent
8
8
  from .appsync_resolver_event import AppSyncResolverEvent
9
+ from .appsync_resolver_events_event import AppSyncResolverEventsEvent
9
10
  from .aws_config_rule_event import AWSConfigRuleEvent
10
11
  from .bedrock_agent_event import BedrockAgentEvent
11
12
  from .cloud_watch_alarm_event import (
@@ -55,6 +56,7 @@ __all__ = [
55
56
  "APIGatewayWebSocketEvent",
56
57
  "SecretsManagerEvent",
57
58
  "AppSyncResolverEvent",
59
+ "AppSyncResolverEventsEvent",
58
60
  "ALBEvent",
59
61
  "BedrockAgentEvent",
60
62
  "CloudWatchAlarmData",
@@ -26,6 +26,46 @@ def get_identity_object(identity: dict | None) -> Any:
26
26
  return AppSyncIdentityIAM(identity)
27
27
 
28
28
 
29
+ class AppSyncEventBase(DictWrapper):
30
+ """AppSync resolver event base to work with AppSync GraphQL + Events"""
31
+
32
+ @property
33
+ def request_headers(self) -> dict[str, str]:
34
+ """Request headers"""
35
+ return CaseInsensitiveDict(self["request"]["headers"])
36
+
37
+ @property
38
+ def domain_name(self) -> str | None:
39
+ """The domain name when using custom domain"""
40
+ return self["request"].get("domainName")
41
+
42
+ @property
43
+ def prev_result(self) -> dict[str, Any] | None:
44
+ """It represents the result of whatever previous operation was executed in a pipeline resolver."""
45
+ prev = self.get("prev")
46
+ return prev.get("result") if prev else None
47
+
48
+ @property
49
+ def stash(self) -> dict:
50
+ """The stash is a map that is made available inside each resolver and function mapping template.
51
+ The same stash instance lives through a single resolver execution. This means that you can use the
52
+ stash to pass arbitrary data across request and response mapping templates, and across functions in
53
+ a pipeline resolver."""
54
+ return self.get("stash") or {}
55
+
56
+ @property
57
+ def identity(self) -> AppSyncIdentityIAM | AppSyncIdentityCognito | None:
58
+ """An object that contains information about the caller.
59
+ Depending on the type of identify found:
60
+ - API_KEY authorization - returns None
61
+ - AWS_IAM authorization - returns AppSyncIdentityIAM
62
+ - AMAZON_COGNITO_USER_POOLS authorization - returns AppSyncIdentityCognito
63
+ - AWS_LAMBDA authorization - returns None - NEED TO TEST
64
+ - OPENID_CONNECT authorization - returns None - NEED TO TEST
65
+ """
66
+ return get_identity_object(self.get("identity"))
67
+
68
+
29
69
  class AppSyncIdentityIAM(DictWrapper):
30
70
  """AWS_IAM authorization"""
31
71
 
@@ -141,7 +181,7 @@ class AppSyncResolverEventInfo(DictWrapper):
141
181
  return self.get("selectionSetGraphQL")
142
182
 
143
183
 
144
- class AppSyncResolverEvent(DictWrapper):
184
+ class AppSyncResolverEvent(AppSyncEventBase):
145
185
  """AppSync resolver event
146
186
 
147
187
  **NOTE:** AppSync Resolver Events can come in various shapes this data class
@@ -178,49 +218,16 @@ class AppSyncResolverEvent(DictWrapper):
178
218
  """A map that contains all GraphQL arguments for this field."""
179
219
  return self["arguments"]
180
220
 
181
- @property
182
- def identity(self) -> AppSyncIdentityIAM | AppSyncIdentityCognito | None:
183
- """An object that contains information about the caller.
184
-
185
- Depending on the type of identify found:
186
-
187
- - API_KEY authorization - returns None
188
- - AWS_IAM authorization - returns AppSyncIdentityIAM
189
- - AMAZON_COGNITO_USER_POOLS authorization - returns AppSyncIdentityCognito
190
- """
191
- return get_identity_object(self.get("identity"))
192
-
193
221
  @property
194
222
  def source(self) -> dict[str, Any]:
195
223
  """A map that contains the resolution of the parent field."""
196
224
  return self.get("source") or {}
197
225
 
198
- @property
199
- def request_headers(self) -> dict[str, str]:
200
- """Request headers"""
201
- return CaseInsensitiveDict(self["request"]["headers"])
202
-
203
- @property
204
- def prev_result(self) -> dict[str, Any] | None:
205
- """It represents the result of whatever previous operation was executed in a pipeline resolver."""
206
- prev = self.get("prev")
207
- if not prev:
208
- return None
209
- return prev.get("result")
210
-
211
226
  @property
212
227
  def info(self) -> AppSyncResolverEventInfo:
213
228
  """The info section contains information about the GraphQL request."""
214
229
  return self._info
215
230
 
216
- @property
217
- def stash(self) -> dict:
218
- """The stash is a map that is made available inside each resolver and function mapping template.
219
- The same stash instance lives through a single resolver execution. This means that you can use the
220
- stash to pass arbitrary data across request and response mapping templates, and across functions in
221
- a pipeline resolver."""
222
- return self.get("stash") or {}
223
-
224
231
  @overload
225
232
  def get_header_value(
226
233
  self,