clear-skies-aws 1.10.2__py3-none-any.whl → 2.0.2__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 (86) hide show
  1. {clear_skies_aws-1.10.2.dist-info → clear_skies_aws-2.0.2.dist-info}/METADATA +36 -35
  2. clear_skies_aws-2.0.2.dist-info/RECORD +63 -0
  3. {clear_skies_aws-1.10.2.dist-info → clear_skies_aws-2.0.2.dist-info}/WHEEL +1 -1
  4. clear_skies_aws-2.0.2.dist-info/licenses/LICENSE +21 -0
  5. clearskies_aws/__init__.py +15 -2
  6. clearskies_aws/actions/__init__.py +13 -106
  7. clearskies_aws/actions/action_aws.py +74 -57
  8. clearskies_aws/actions/assume_role.py +43 -30
  9. clearskies_aws/actions/ses.py +82 -73
  10. clearskies_aws/actions/sns.py +27 -30
  11. clearskies_aws/actions/sqs.py +32 -33
  12. clearskies_aws/actions/step_function.py +38 -31
  13. clearskies_aws/backends/__init__.py +11 -4
  14. clearskies_aws/backends/backend.py +106 -0
  15. clearskies_aws/backends/dynamo_db_backend.py +150 -155
  16. clearskies_aws/backends/dynamo_db_condition_parser.py +40 -80
  17. clearskies_aws/backends/dynamo_db_parti_ql_backend.py +179 -337
  18. clearskies_aws/backends/sqs_backend.py +32 -51
  19. clearskies_aws/configs/__init__.py +0 -0
  20. clearskies_aws/contexts/__init__.py +23 -10
  21. clearskies_aws/contexts/cli_web_socket_mock.py +19 -0
  22. clearskies_aws/contexts/lambda_alb.py +76 -0
  23. clearskies_aws/contexts/lambda_api_gateway.py +75 -28
  24. clearskies_aws/contexts/lambda_api_gateway_web_socket.py +56 -29
  25. clearskies_aws/contexts/lambda_invocation.py +15 -44
  26. clearskies_aws/contexts/lambda_sns.py +8 -33
  27. clearskies_aws/contexts/lambda_sqs_standard_partial_batch.py +14 -36
  28. clearskies_aws/di/__init__.py +6 -1
  29. clearskies_aws/di/aws_additional_config_auto_import.py +37 -0
  30. clearskies_aws/di/inject/__init__.py +6 -0
  31. clearskies_aws/di/inject/boto3.py +15 -0
  32. clearskies_aws/di/inject/boto3_session.py +13 -0
  33. clearskies_aws/di/inject/parameter_store.py +15 -0
  34. clearskies_aws/{handlers → endpoints}/secrets_manager_rotation.py +76 -55
  35. clearskies_aws/endpoints/simple_body_routing.py +41 -0
  36. clearskies_aws/input_outputs/__init__.py +21 -8
  37. clearskies_aws/input_outputs/{cli_websocket_mock.py → cli_web_socket_mock.py} +9 -3
  38. clearskies_aws/input_outputs/lambda_alb.py +53 -0
  39. clearskies_aws/input_outputs/lambda_api_gateway.py +106 -88
  40. clearskies_aws/input_outputs/lambda_api_gateway_web_socket.py +69 -6
  41. clearskies_aws/input_outputs/lambda_input_output.py +87 -0
  42. clearskies_aws/input_outputs/lambda_invocation.py +77 -26
  43. clearskies_aws/input_outputs/lambda_sns.py +66 -39
  44. clearskies_aws/input_outputs/lambda_sqs_standard.py +70 -40
  45. clearskies_aws/mocks/actions/ses.py +25 -19
  46. clearskies_aws/mocks/actions/sns.py +18 -12
  47. clearskies_aws/mocks/actions/sqs.py +18 -12
  48. clearskies_aws/mocks/actions/step_function.py +19 -13
  49. clearskies_aws/models/__init__.py +0 -0
  50. clearskies_aws/models/web_socket_connection_model.py +182 -0
  51. clearskies_aws/secrets/__init__.py +13 -7
  52. clearskies_aws/secrets/additional_configs/__init__.py +10 -2
  53. clearskies_aws/secrets/additional_configs/iam_db_auth.py +26 -16
  54. clearskies_aws/secrets/additional_configs/iam_db_auth_with_ssm.py +43 -39
  55. clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +30 -31
  56. clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssm_bastion.py +70 -49
  57. clearskies_aws/secrets/akeyless_with_ssm_cache.py +32 -18
  58. clearskies_aws/secrets/parameter_store.py +34 -32
  59. clearskies_aws/secrets/secrets.py +16 -0
  60. clearskies_aws/secrets/secrets_manager.py +78 -57
  61. clear_skies_aws-1.10.2.dist-info/LICENSE +0 -7
  62. clear_skies_aws-1.10.2.dist-info/RECORD +0 -71
  63. clearskies_aws/actions/assume_role_test.py +0 -72
  64. clearskies_aws/actions/ses_test.py +0 -89
  65. clearskies_aws/actions/sns_test.py +0 -77
  66. clearskies_aws/actions/sqs_test.py +0 -127
  67. clearskies_aws/actions/step_function_test.py +0 -103
  68. clearskies_aws/backends/dynamo_db_backend_test.py +0 -300
  69. clearskies_aws/backends/dynamo_db_condition_parser_test.py +0 -266
  70. clearskies_aws/backends/dynamo_db_parti_ql_backend_test.py +0 -544
  71. clearskies_aws/backends/sqs_backend_test.py +0 -31
  72. clearskies_aws/contexts/cli.py +0 -19
  73. clearskies_aws/contexts/cli_websocket_mock.py +0 -33
  74. clearskies_aws/contexts/lambda_elb.py +0 -30
  75. clearskies_aws/contexts/lambda_http_gateway.py +0 -30
  76. clearskies_aws/contexts/lambda_sqs_standard_partial_batch_test.py +0 -66
  77. clearskies_aws/contexts/wsgi.py +0 -19
  78. clearskies_aws/di/standard_dependencies.py +0 -60
  79. clearskies_aws/handlers/simple_body_routing.py +0 -39
  80. clearskies_aws/input_outputs/lambda_api_gateway_test.py +0 -87
  81. clearskies_aws/input_outputs/lambda_elb.py +0 -21
  82. clearskies_aws/input_outputs/lambda_http_gateway.py +0 -12
  83. clearskies_aws/secrets/parameter_store_test.py +0 -18
  84. clearskies_aws/secrets/secrets_manager_test.py +0 -18
  85. clearskies_aws/web_socket_connection_model.py +0 -43
  86. clearskies_aws/{handlers → endpoints}/__init__.py +1 -1
@@ -1,10 +1,18 @@
1
- from clearskies.backends.backend import Backend
2
- from clearskies import model
1
+ from __future__ import annotations
2
+
3
3
  import json
4
- from typing import Any, Callable, Dict, List, Tuple
5
- class SqsBackend(Backend):
4
+ from typing import Any
5
+
6
+ from clearskies import Model
7
+ from clearskies.query import Query
8
+ from types_boto3_sqs import SQSClient
9
+
10
+ from clearskies_aws.backends import backend
11
+
12
+
13
+ class SqsBackend(backend.Backend):
6
14
  """
7
- SQS backend for clearskies
15
+ SQS backend for clearskies.
8
16
 
9
17
  There's not too much to this. Just set it on your model and set the table name equal to the SQS url.
10
18
 
@@ -17,64 +25,37 @@ class SqsBackend(Backend):
17
25
  See the SQS context in this library for processing your queue data.
18
26
  """
19
27
 
20
- _boto3 = None
21
- _environment = None
22
- _sqs = None
23
-
24
- _allowed_configs = [
25
- 'table_name',
26
- 'model_columns',
27
- ]
28
+ _sqs: SQSClient
28
29
 
29
- _required_configs = [
30
- 'table_name',
31
- ]
30
+ @property
31
+ def sqs(self) -> SQSClient:
32
+ if not hasattr(self, "_sqs"):
33
+ if not self.environment.get("AWS_REGION", True):
34
+ raise ValueError("To use SQS you must use set AWS_REGION in the .env file or an environment variable")
32
35
 
33
- def __init__(self, boto3, environment):
34
- self._boto3 = boto3
35
- self._environment = environment
36
- if not environment.get('AWS_REGION', True):
37
- raise ValueError('To use SQS you must use set AWS_REGION in the .env file or an environment variable')
36
+ self._sqs = self.boto3.client("sqs", region_name=self.environment.get("AWS_REGION", True))
38
37
 
39
- self._sqs = self._boto3.client('sqs', region_name=environment.get('AWS_REGION', True))
38
+ return self._sqs
40
39
 
41
- def configure(self):
42
- pass
43
-
44
- def create(self, data, model):
45
- self._sqs.send_message(
46
- QueueUrl=model.table_name(),
40
+ def create(self, data: dict[str, Any], model: Model) -> dict[str, Any]:
41
+ self.sqs.send_message(
42
+ QueueUrl=model.destination_name(),
47
43
  MessageBody=json.dumps(data),
48
44
  )
49
45
  return {**data}
50
46
 
51
- def update(self, id, data, model):
47
+ def update(self, id: int | str, data: dict[str, Any], model: Model) -> dict[str, Any]:
52
48
  raise ValueError("The SQS backend only supports the create operation")
53
49
 
54
- def delete(self, id, model):
50
+ def delete(self, id: int | str, model: Model) -> bool:
55
51
  raise ValueError("The SQS backend only supports the create operation")
56
52
 
57
- def count(self, configuration, model):
53
+ def count(self, query: Query) -> int:
58
54
  raise ValueError("The SQS backend only supports the create operation")
59
55
 
60
- def records(self,
61
- configuration: Dict[str, Any],
62
- model: model.Model,
63
- next_page_data: Dict[str, str] = None) -> List[Dict[str, Any]]:
56
+ def records(
57
+ self,
58
+ query: Query,
59
+ next_page_data: dict[str, str | int] | None = None,
60
+ ) -> list[dict[str, Any]]:
64
61
  raise ValueError("The SQS backend only supports the create operation")
65
- return []
66
-
67
- def validate_pagination_kwargs(self, kwargs: Dict[str, Any], case_mapping: Callable) -> str:
68
- return ''
69
-
70
- def allowed_pagination_keys(self) -> List[str]:
71
- return []
72
-
73
- def documentation_pagination_next_page_response(self, case_mapping: Callable) -> List[Any]:
74
- return []
75
-
76
- def documentation_pagination_next_page_example(self, case_mapping: Callable) -> Dict[str, Any]:
77
- return {}
78
-
79
- def documentation_pagination_parameters(self, case_mapping: Callable) -> List[Tuple[Any]]:
80
- return []
File without changes
@@ -1,10 +1,23 @@
1
- from .cli import cli
2
- from .cli_websocket_mock import cli_websocket_mock
3
- from .lambda_api_gateway import lambda_api_gateway
4
- from .lambda_api_gateway_web_socket import lambda_api_gateway_web_socket
5
- from .lambda_elb import lambda_elb
6
- from .lambda_http_gateway import lambda_http_gateway
7
- from .lambda_invocation import lambda_invocation
8
- from .lambda_sqs_standard_partial_batch import lambda_sqs_standard_partial_batch
9
- from .lambda_sns import lambda_sns
10
- from .wsgi import wsgi
1
+ from __future__ import annotations
2
+
3
+ from clearskies_aws.contexts.cli_web_socket_mock import CliWebSocketMock
4
+ from clearskies_aws.contexts.lambda_alb import LambdaAlb
5
+ from clearskies_aws.contexts.lambda_api_gateway import LambdaApiGateway
6
+ from clearskies_aws.contexts.lambda_api_gateway_web_socket import (
7
+ LambdaApiGatewayWebSocket,
8
+ )
9
+ from clearskies_aws.contexts.lambda_invocation import LambdaInvocation
10
+ from clearskies_aws.contexts.lambda_sns import LambdaSns
11
+ from clearskies_aws.contexts.lambda_sqs_standard_partial_batch import (
12
+ LambdaSqsStandardPartialBatch,
13
+ )
14
+
15
+ __all__ = [
16
+ "CliWebSocketMock",
17
+ "LambdaAlb",
18
+ "LambdaApiGateway",
19
+ "LambdaApiGatewayWebSocket",
20
+ "LambdaInvocation",
21
+ "LambdaSns",
22
+ "LambdaSqsStandardPartialBatch",
23
+ ]
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.contexts import cli
4
+
5
+ from clearskies_aws.input_outputs import CliWebSocketMock as CliWebSocketMockInputOutput
6
+
7
+
8
+ class CliWebSocketMock(cli.Cli):
9
+ """
10
+ Help assist with testing websockets locally.
11
+
12
+ The LambdaApiGatewayWebSocket context makes it easy to run websocket applications, but testing
13
+ these locally is literally impossible. This context provides a close analogue to the way
14
+ the LambdaApiGatewayWebSocket context works to give some testing capabilities when running
15
+ locally.
16
+ """
17
+
18
+ def __call__(self):
19
+ return self.execute_application(CliWebSocketMockInputOutput())
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from clearskies.contexts.context import Context
6
+
7
+ from clearskies_aws.input_outputs import LambdaAlb as LambdaAlbInputOutput
8
+
9
+
10
+ class LambdaAlb(Context):
11
+ """
12
+ Run a clearskies application in a lambda behind an application load balancer.
13
+
14
+ There's nothing special here: just build your application, use the LambdaAlb context in a standard AWS lambda
15
+ handler, and attach your lambda to an ALB. This generally expects that the ALB will forward all requests to
16
+ the clearskies application, which will therefore handle all routing. However, you can also use path-based
17
+ routing in your target group to forward some subset of requests to separate lambdas, each using this same
18
+ context. When you do this, keep in mind that AWS still passes along the full path (including the part handled
19
+ by the ALB), so you want to make sure that your clearskies application is configured with the full URL as well.
20
+
21
+ Per AWS norms, you should create the context in the "root" of your python application, and then invoke it
22
+ inside a standard lambda handler function. This will allow AWS to cache the full application, improving
23
+ performance. If you create and invoke the context inside of your lambda handler, it will effectively turn
24
+ off any caching. In addition, clearskies does a fair amount of configuration validation when you create the
25
+ context, so this work will be repeated on every call.
26
+
27
+ ```
28
+ import clearskies
29
+ import clearskies_aws
30
+ from clearskies.validators import Required, Unique
31
+ from clearskies import columns
32
+
33
+
34
+ class User(clearskies.Model):
35
+ id_column_name = "id"
36
+ backend = clearskies.backends.MemoryBackend()
37
+
38
+ id = columns.Uuid()
39
+ name = columns.String(validators=[Required()])
40
+ username = columns.String(
41
+ validators=[
42
+ Required(),
43
+ Unique(),
44
+ ]
45
+ )
46
+ age = columns.Integer(validators=[Required()])
47
+ created_at = columns.Created()
48
+ updated_at = columns.Updated()
49
+
50
+
51
+ application = clearskies_aws.contexts.LambdaAlb(
52
+ clearskies.endpoints.RestfulApi(
53
+ url="users",
54
+ model_class=User,
55
+ readable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
56
+ writeable_column_names=["name", "username", "age"],
57
+ sortable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
58
+ searchable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
59
+ default_sort_column_name="name",
60
+ )
61
+ )
62
+
63
+
64
+ def lambda_handler(event, context):
65
+ return application(event, context)
66
+ ```
67
+
68
+ ### Context for Callables
69
+
70
+ When using this context, two additional named arguments become available to any callables invoked by clearskies:
71
+ `event` and `context`. These correspond to the original `event` and `context` variables provided by AWS to
72
+ the lambda.
73
+ """
74
+
75
+ def __call__(self, event: dict[str, Any], context: dict[str, Any]) -> Any: # type: ignore[override]
76
+ return self.execute_application(LambdaAlbInputOutput(event, context))
@@ -1,30 +1,77 @@
1
- from ..input_outputs import LambdaAPIGateway as LambdaAPIGatewayInputOutput
2
- from ..di import StandardDependencies
3
- from clearskies.contexts.build_context import build_context
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
4
5
  from clearskies.contexts.context import Context
5
- class LambdaAPIGateway(Context):
6
- def __init__(self, di):
7
- super().__init__(di)
8
-
9
- def __call__(self, event, context):
10
- if self.handler is None:
11
- raise ValueError("Cannot execute LambdaAPIGateway context without first configuring it")
12
-
13
- return self.handler(LambdaAPIGatewayInputOutput(event, context))
14
- def lambda_api_gateway(
15
- application,
16
- di_class=StandardDependencies,
17
- bindings=None,
18
- binding_classes=None,
19
- binding_modules=None,
20
- additional_configs=None,
21
- ):
22
- return build_context(
23
- LambdaAPIGateway,
24
- application,
25
- di_class=di_class,
26
- bindings=bindings,
27
- binding_classes=binding_classes,
28
- binding_modules=binding_modules,
29
- additional_configs=additional_configs,
6
+
7
+ from clearskies_aws.input_outputs import LambdaApiGateway as LambdaApiGatewayInputOutput
8
+
9
+
10
+ class LambdaApiGateway(Context):
11
+ """
12
+ Run a clearskies application in a lambda behind an API Gateway (v1 or v2).
13
+
14
+ There's nothing special here: just build your application, use the LambdaApiGateway context in a standard AWS
15
+ lambda handler, and attach your lambda to an Api Gateway. Per AWS norms, you should create the context in
16
+ the "root" of your python application, and then invoke it inside a standard lambda handler function. This
17
+ will allow AWS to cache the full application, improving performance. If you create and invoke the context
18
+ inside of your lambda handler, it will effectively turn off any caching. In addition, clearskies does a fair
19
+ amount of configuration validation when you create the context, so this work will be repeated on every call.
20
+
21
+ ```
22
+ import clearskies
23
+ import clearskies_aws
24
+ from clearskies.validators import Required, Unique
25
+ from clearskies import columns
26
+
27
+
28
+ class User(clearskies.Model):
29
+ id_column_name = "id"
30
+ backend = clearskies.backends.MemoryBackend()
31
+
32
+ id = columns.Uuid()
33
+ name = columns.String(validators=[Required()])
34
+ username = columns.String(
35
+ validators=[
36
+ Required(),
37
+ Unique(),
38
+ ]
39
+ )
40
+ age = columns.Integer(validators=[Required()])
41
+ created_at = columns.Created()
42
+ updated_at = columns.Updated()
43
+
44
+
45
+ application = clearskies_aws.contexts.LambdaApiGateway(
46
+ clearskies.endpoints.RestfulApi(
47
+ url="users",
48
+ model_class=User,
49
+ readable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
50
+ writeable_column_names=["name", "username", "age"],
51
+ sortable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
52
+ searchable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
53
+ default_sort_column_name="name",
54
+ )
30
55
  )
56
+
57
+
58
+ def lambda_handler(event, context):
59
+ return application(event, context)
60
+ ```
61
+
62
+ ### Context for Callables
63
+
64
+ When using this context, a number of additional named arguments become available to any callables invoked by
65
+ clearskies:
66
+
67
+ 1. `event`
68
+ 2. `context`
69
+ 3. `resource`
70
+ 4. `stage`
71
+ 5. `request_id`
72
+ 6. `api_id`
73
+ 7. `api_version` (v1 or v2)
74
+ """
75
+
76
+ def __call__(self, event: dict[str, Any], context: dict[str, Any]) -> Any: # type: ignore[override]
77
+ return self.execute_application(LambdaApiGatewayInputOutput(event, context))
@@ -1,30 +1,57 @@
1
- from ..input_outputs import LambdaAPIGatewayWebSocket as LambdaAPIGatewayWebSocketInputOutput
2
- from ..di import StandardDependencies
3
- from clearskies.contexts.build_context import build_context
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
4
5
  from clearskies.contexts.context import Context
5
- class LambdaAPIGatewayWebSocket(Context):
6
- def __init__(self, di):
7
- super().__init__(di)
8
-
9
- def __call__(self, event, context):
10
- if self.handler is None:
11
- raise ValueError("Cannot execute LambdaAPIGatewayWebSocket context without first configuring it")
12
-
13
- return self.handler(LambdaAPIGatewayWebSocketInputOutput(event, context))
14
- def lambda_api_gateway_web_socket(
15
- application,
16
- di_class=StandardDependencies,
17
- bindings=None,
18
- binding_classes=None,
19
- binding_modules=None,
20
- additional_configs=None,
21
- ):
22
- return build_context(
23
- LambdaAPIGatewayWebSocket,
24
- application,
25
- di_class=di_class,
26
- bindings=bindings,
27
- binding_classes=binding_classes,
28
- binding_modules=binding_modules,
29
- additional_configs=additional_configs,
30
- )
6
+
7
+ from clearskies_aws.input_outputs import (
8
+ LambdaApiGatewayWebSocket as LambdaApiGatewayWebSocketInputOutput,
9
+ )
10
+
11
+
12
+ class LambdaApiGatewayWebSocket(Context):
13
+ """
14
+ Run a clearskies application behind an API Gateway that is configured for use as a websocket.
15
+
16
+ Websockets work much differently than standard API endpoints. Most importantly, none of the standard HTTP
17
+ concepts exist. Websockets requests don't have any of:
18
+
19
+ 1. URL Path
20
+ 2. Query Parameters
21
+ 3. HTTP Headers
22
+ 4. Response Headers
23
+ 5. An HTTP Response
24
+
25
+ So in short, everything works completely differently. The reason is because a websocket is a
26
+ two-way communication channel that's created over a TCP/IP connection. It does start with an HTTP request,
27
+ but this is a one time request when the communication channel is first created. Later messages (which
28
+ are where the bulk of the communication happens) travel over the already-open connection, so
29
+ the communication looks nothing like HTTP. Usually, the data traveling over this connection is
30
+ a JSON payload, and since the connection is already opened it doesn't have any of the metadata associated
31
+ with an HTTP request (hence the lack of url/query/headers). In addition, the communication is no longer
32
+ transactional - messages from the client to the server do not come with a direct response, and the server
33
+ can send messages to the client without needing the former to initiate the conversation.
34
+
35
+ Routing and authorization are usually handled in-band, which means that the routing parameters or authentication
36
+ data are added directly to the JSON body sent over the open connection. This often results in applications
37
+ having to handle such things themselves, since the typical standards of web frameworks won't match up. In
38
+ the case of routing with an API Gateway, it has its own suggested standard of setting a routekey where the
39
+ API gateway will check for an application-defined route parameter in the request body and use this to route
40
+ to an appropriate lambda. With clearskies, you can also use the `clearskies.endpoints.JsonParamEndpointGroup`
41
+ to accomplish the same.
42
+
43
+ With a websocket through API Gateway, headers are available during the `on_connect` phase, so you can always
44
+ perform authentication then and record the result with the connection id (which can be used much like a
45
+ session id). Otherwise, authentication is typically handled by including the authentication token in every
46
+ message payload.
47
+
48
+ ### Sending Messages
49
+
50
+ An important part of using websockets is being able to manage and send messages to clients. To help with this,
51
+ there is a base model class in `clearskies_aws.models.WebSocketConnectionModel`. Check the documentation for
52
+ this class to understand how this is managed and see a "starter" websocket application.
53
+
54
+ """
55
+
56
+ def __call__(self, event: dict[str, Any], context: dict[str, Any], url: str = "") -> Any: # type: ignore[override]
57
+ return self.execute_application(LambdaApiGatewayWebSocketInputOutput(event, context, url))
@@ -1,48 +1,19 @@
1
- from clearskies.authentication import public
2
- from ..input_outputs import LambdaInvocation as LambdaInvocationInputOutput
3
- from ..di import StandardDependencies
4
- from clearskies.contexts.build_context import build_context
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from clearskies.authentication import Public
5
6
  from clearskies.contexts.context import Context
6
- class LambdaInvocation(Context):
7
- def __init__(self, di):
8
- super().__init__(di)
9
7
 
10
- def finalize_handler_config(self, config):
11
- return {
12
- 'authentication': public(),
13
- **config,
14
- }
8
+ from clearskies_aws.input_outputs import LambdaInvocation as LambdaInvocationInputOutput
15
9
 
16
- def __call__(
17
- self,
18
- event,
19
- context,
20
- method=None,
21
- url=None,
22
- ):
23
- if self.handler is None:
24
- raise ValueError("Cannot execute LambdaInvocation context without first configuring it")
25
10
 
26
- return self.handler(LambdaInvocationInputOutput(
27
- event,
28
- context,
29
- method=method,
30
- url=url,
31
- ))
32
- def lambda_invocation(
33
- application,
34
- di_class=StandardDependencies,
35
- bindings=None,
36
- binding_classes=None,
37
- binding_modules=None,
38
- additional_configs=None,
39
- ):
40
- return build_context(
41
- LambdaInvocation,
42
- application,
43
- di_class=di_class,
44
- bindings=bindings,
45
- binding_classes=binding_classes,
46
- binding_modules=binding_modules,
47
- additional_configs=additional_configs,
48
- )
11
+ class LambdaInvocation(Context):
12
+
13
+ def __call__(self, event: dict[str, Any], context: dict[str, Any]) -> Any: # type: ignore[override]
14
+ return self.execute_application(
15
+ LambdaInvocationInputOutput(
16
+ event,
17
+ context,
18
+ )
19
+ )
@@ -1,43 +1,18 @@
1
- from ..input_outputs import LambdaSns as LambdaSnsInputOutput
2
- from ..di import StandardDependencies
3
- from clearskies.contexts.build_context import build_context
1
+ from __future__ import annotations
2
+
3
+ from clearskies.authentication import Public
4
4
  from clearskies.contexts.context import Context
5
- from clearskies.authentication import public
6
5
 
7
- class LambdaSns(Context):
8
- def __init__(self, di):
9
- super().__init__(di)
6
+ from clearskies_aws.input_outputs import LambdaSns as LambdaSnsInputOutput
10
7
 
11
- def finalize_handler_config(self, config):
12
- return {
13
- 'authentication': public(),
14
- **config,
15
- }
16
8
 
9
+ class LambdaSns(Context):
17
10
  def __call__(self, event, context, method=None, url=None):
18
- if self.handler is None:
11
+ if self.execute_application is None:
19
12
  raise ValueError("Cannot execute LambdaSnsEvent context without first configuring it")
20
13
 
21
14
  try:
22
- return self.handler(LambdaSnsInputOutput(event, context, method=method, url=url))
15
+ return self.execute_application(LambdaSnsInputOutput(event, context, method=method, url=url))
23
16
  except Exception as e:
24
- print('Failed message ' + event['Records'][0]['Sns']['MessageId'] + '. Error error: ' + str(e))
17
+ print("Failed message " + event["Records"][0]["Sns"]["MessageId"] + ". Error error: " + str(e))
25
18
  raise e
26
-
27
- def lambda_sns(
28
- application,
29
- di_class=StandardDependencies,
30
- bindings=None,
31
- binding_classes=None,
32
- binding_modules=None,
33
- additional_configs=None,
34
- ):
35
- return build_context(
36
- LambdaSns,
37
- application,
38
- di_class=di_class,
39
- bindings=bindings,
40
- binding_classes=binding_classes,
41
- binding_modules=binding_modules,
42
- additional_configs=additional_configs,
43
- )
@@ -1,51 +1,29 @@
1
+ from __future__ import annotations
2
+
1
3
  import traceback
2
- from ..input_outputs import LambdaSqsStandard as LambdaSqsStandardInputOutput
3
- from ..di import StandardDependencies
4
- from clearskies.contexts.build_context import build_context
4
+
5
+ from clearskies.authentication import Public
5
6
  from clearskies.contexts.context import Context
6
- from clearskies.authentication import public
7
- class LambdaSqsStandardPartialBatch(Context):
8
- def __init__(self, di):
9
- super().__init__(di)
10
7
 
11
- def finalize_handler_config(self, config):
12
- return {
13
- 'authentication': public(),
14
- **config,
15
- }
8
+ from clearskies_aws.input_outputs import LambdaSqsStandard as LambdaSqsStandardInputOutput
16
9
 
17
- def __call__(self, event, context, url=None, method=None):
18
- if self.handler is None:
19
- raise ValueError("Cannot execute LambdaELB context without first configuring it")
20
10
 
11
+ class LambdaSqsStandardPartialBatch(Context):
12
+ def __call__(self, event, context, url="", method="POST"):
21
13
  item_failures = []
22
- for record in event['Records']:
14
+ for record in event["Records"]:
15
+ print("Processing message " + record["messageId"], record["body"])
23
16
  try:
24
- self.handler(LambdaSqsStandardInputOutput(record['body'], event, context, url=url, method=method))
17
+ self.execute_application(
18
+ LambdaSqsStandardInputOutput(record["body"], event, context, url=url, method=method)
19
+ )
25
20
  except Exception as e:
26
- print('Failed message ' + record['messageId'] + ' being returned for retry. Error error: ' + str(e))
21
+ print("Failed message " + record["messageId"] + " being returned for retry. Error error: " + str(e))
27
22
  traceback.print_tb(e.__traceback__)
28
- item_failures.append({'itemIdentifier': record['messageId']})
23
+ item_failures.append({"itemIdentifier": record["messageId"]})
29
24
 
30
25
  if item_failures:
31
26
  return {
32
27
  "batchItemFailures": item_failures,
33
28
  }
34
29
  return {}
35
- def lambda_sqs_standard_partial_batch(
36
- application,
37
- di_class=StandardDependencies,
38
- bindings=None,
39
- binding_classes=None,
40
- binding_modules=None,
41
- additional_configs=None,
42
- ):
43
- return build_context(
44
- LambdaSqsStandardPartialBatch,
45
- application,
46
- di_class=di_class,
47
- bindings=bindings,
48
- binding_classes=binding_classes,
49
- binding_modules=binding_modules,
50
- additional_configs=additional_configs,
51
- )
@@ -1 +1,6 @@
1
- from .standard_dependencies import StandardDependencies
1
+ from __future__ import annotations
2
+
3
+ from clearskies_aws.di import inject
4
+ from clearskies_aws.di.aws_additional_config_auto_import import AwsAdditionalConfigAutoImport
5
+
6
+ __all__ = ["inject", "AwsAdditionalConfigAutoImport"]
@@ -0,0 +1,37 @@
1
+ import datetime
2
+ from types import ModuleType
3
+ from typing import Any
4
+
5
+ import boto3 as boto3_module
6
+ from clearskies import Environment
7
+ from clearskies.di import AdditionalConfigAutoImport
8
+ from clearskies.di.additional_config import AdditionalConfig
9
+
10
+ from clearskies_aws.secrets import ParameterStore
11
+
12
+
13
+ class AwsAdditionalConfigAutoImport(AdditionalConfigAutoImport):
14
+ """
15
+ Provide a DI with AWS modules built-in.
16
+
17
+ This DI auto injects boto3, boto3 Session and the parameter store.
18
+ """
19
+
20
+ def provide_boto3(self) -> ModuleType:
21
+ import boto3
22
+
23
+ return boto3
24
+
25
+ def provide_parameter_store(self) -> ParameterStore:
26
+ # This is just here so that we can auto-inject the secrets into the environment without having
27
+ # to force the developer to define a secrets manager
28
+ return ParameterStore()
29
+
30
+ def provide_boto3_session(self, boto3: ModuleType, environment: Environment) -> boto3_module.session.Session:
31
+ if not environment.get("AWS_REGION", True):
32
+ raise ValueError(
33
+ "To use AWS Session you must use set AWS_REGION in the .env file or an environment variable"
34
+ )
35
+
36
+ session = boto3.session.Session(region_name=environment.get("AWS_REGION", True))
37
+ return session