clear-skies-aws 2.0.11__py3-none-any.whl → 2.0.12__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.
- {clear_skies_aws-2.0.11.dist-info → clear_skies_aws-2.0.12.dist-info}/METADATA +2 -2
- {clear_skies_aws-2.0.11.dist-info → clear_skies_aws-2.0.12.dist-info}/RECORD +10 -8
- clearskies_aws/backends/backend.py +17 -21
- clearskies_aws/backends/sqs_backend.py +7 -10
- clearskies_aws/contexts/__init__.py +2 -0
- clearskies_aws/contexts/lambda_step_function.py +129 -0
- clearskies_aws/input_outputs/__init__.py +2 -0
- clearskies_aws/input_outputs/lambda_step_function.py +208 -0
- {clear_skies_aws-2.0.11.dist-info → clear_skies_aws-2.0.12.dist-info}/WHEEL +0 -0
- {clear_skies_aws-2.0.11.dist-info → clear_skies_aws-2.0.12.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clear-skies-aws
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.12
|
|
4
4
|
Summary: clearskies bindings for working in AWS
|
|
5
5
|
Project-URL: Repository, https://github.com/clearskies-py/clearskies-aws
|
|
6
6
|
Project-URL: Issues, https://github.com/clearskies-py/clearskies-aws/issues
|
|
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Requires-Python: <4.0,>=3.11
|
|
18
18
|
Requires-Dist: awslambdaric>=3.1.1
|
|
19
19
|
Requires-Dist: boto3<2.0.0,>=1.26.148
|
|
20
|
-
Requires-Dist: clear-skies<3.0.0,>=2.0.
|
|
20
|
+
Requires-Dist: clear-skies<3.0.0,>=2.0.37
|
|
21
21
|
Requires-Dist: jinja2>=3.1.6
|
|
22
22
|
Requires-Dist: types-boto3[dynamodb,secretsmanager,ses,sns,sqs,ssm,stepfunctions]<2.0.0,>=1.38.13
|
|
23
23
|
Provides-Extra: akeyless
|
|
@@ -7,13 +7,13 @@ clearskies_aws/actions/sns.py,sha256=YS1TbEwtU-0lDbjG2HyTBs2J-ML5OL3ModAiGTMeK-c
|
|
|
7
7
|
clearskies_aws/actions/sqs.py,sha256=r0z8njU87n09UgAq3l34JuNIbaBE85D_z8IE6ciIs9Q,3359
|
|
8
8
|
clearskies_aws/actions/step_function.py,sha256=Y6tGbQJIAD_IwV9ohwYfuv3vqtryTwpFTNGUFgdj_DQ,2689
|
|
9
9
|
clearskies_aws/backends/__init__.py,sha256=lmjWPoLN7Ebmdj3Pv5X1tJDcRWN3PsR8aGG9nDcqOrs,635
|
|
10
|
-
clearskies_aws/backends/backend.py,sha256=
|
|
10
|
+
clearskies_aws/backends/backend.py,sha256=uxGlThqp2vbCcvQPimFwwPKaF0oqHAjmTgymmjOoTqc,4224
|
|
11
11
|
clearskies_aws/backends/dynamo_db_backend.py,sha256=0KfAow5pUXpiBg0rRWtG3j48mpHUtJP9q-C2FfpBeqA,29411
|
|
12
12
|
clearskies_aws/backends/dynamo_db_condition_parser.py,sha256=796BhrqqZy_lW4wVYbDKK85oVVKa0YnWbtl4Bb0RNF0,12637
|
|
13
13
|
clearskies_aws/backends/dynamo_db_parti_ql_backend.py,sha256=p0SdrRq467w0w5OxYscxPfqUOjrkZ41JLmJa3KQCXq8,46097
|
|
14
|
-
clearskies_aws/backends/sqs_backend.py,sha256=
|
|
14
|
+
clearskies_aws/backends/sqs_backend.py,sha256=uOGi8UeqBsReTDdaCA2X-V4lo4pXpyTsaz1WHHetPYQ,2238
|
|
15
15
|
clearskies_aws/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
clearskies_aws/contexts/__init__.py,sha256=
|
|
16
|
+
clearskies_aws/contexts/__init__.py,sha256=kHru0DK5wpu8Lgx2umVPwqQz5rYRvA50Brnw8HxPgfU,819
|
|
17
17
|
clearskies_aws/contexts/cli_web_socket_mock.py,sha256=XjsFcx0YlQqjb9Hh8q2NUW7T2Exu7_FHx9v_Am0Uq0g,784
|
|
18
18
|
clearskies_aws/contexts/lambda_alb.py,sha256=QvVlBPL1bi_fCYEpq84tGWAfLktu8lm_Atqd6pb_ksw,3496
|
|
19
19
|
clearskies_aws/contexts/lambda_api_gateway.py,sha256=e4g3RNc9rdQndxlyGh7VV1Sh6RROgbWei1lxOzGPB5k,3424
|
|
@@ -21,6 +21,7 @@ clearskies_aws/contexts/lambda_api_gateway_web_socket.py,sha256=G84U8KvLWx9W_J11
|
|
|
21
21
|
clearskies_aws/contexts/lambda_invoke.py,sha256=TBu-SqZyBiXDOz_vrKiR1Kdg0HoVZ-8xhXDOti-SBr0,4942
|
|
22
22
|
clearskies_aws/contexts/lambda_sns.py,sha256=wxrT6bOCp2T8aUmmOhVcTiZ6MXkoGUrjCspsu7XiK8E,4213
|
|
23
23
|
clearskies_aws/contexts/lambda_sqs_standard.py,sha256=S6bdSshZeKpssHcNXpxaOEWU_jw7LBA78H9d8Mwdy3o,5363
|
|
24
|
+
clearskies_aws/contexts/lambda_step_function.py,sha256=pgDuaFuyWQOLJRgXIL2axCsT-zFRcGBjDJpZJ-ojxvo,4334
|
|
24
25
|
clearskies_aws/cursors/__init__.py,sha256=vy-WYxNEy5nd9fqY0En3T6WhE3omoV1tE-A5Uz26cCk,94
|
|
25
26
|
clearskies_aws/cursors/iam/__init__.py,sha256=dY410gfPoMXB42jhmamqD5IiCeopDnxr5wIibpqaYcY,127
|
|
26
27
|
clearskies_aws/cursors/iam/rds_mysql.py,sha256=bdAYTLg9t8DsoSi0otY9bmIgLCbrezczxd_3hWuEkeE,5639
|
|
@@ -35,7 +36,7 @@ clearskies_aws/di/inject/parameter_store.py,sha256=g0uAVwQEywLO9bCcYLbDKuyYnYgVy
|
|
|
35
36
|
clearskies_aws/endpoints/__init__.py,sha256=OUL_nhtuNs62BvQeVtC9xP_e9Hs_-qjANvb81vdLdrc,61
|
|
36
37
|
clearskies_aws/endpoints/secrets_manager_rotation.py,sha256=c__4N-Z4qZ9XkzMMyIcxyPjOIxdO801FAJp6oX6n0a4,7761
|
|
37
38
|
clearskies_aws/endpoints/simple_body_routing.py,sha256=B3fnfxUEMuNpzc26Pzly6618DFU9fpaCk8KhTGSaptE,1181
|
|
38
|
-
clearskies_aws/input_outputs/__init__.py,sha256=
|
|
39
|
+
clearskies_aws/input_outputs/__init__.py,sha256=M7eVt-sqwXYOS5atXbWjQTp3FogrhTcg6c5hH_9GrJo,850
|
|
39
40
|
clearskies_aws/input_outputs/cli_web_socket_mock.py,sha256=cp0MaJjVnsXE1rx5K44lpN9uHKo3MOAsNxVQ3AsJOi4,547
|
|
40
41
|
clearskies_aws/input_outputs/lambda_alb.py,sha256=K298vSqqXzTR1luWRi5lwFL_EvzrIku1wHv8og23ulw,2083
|
|
41
42
|
clearskies_aws/input_outputs/lambda_api_gateway.py,sha256=OeXgjAUMKnURnkgcqTgiZE8zRFfOdDrRxLCCv1gvvsA,4742
|
|
@@ -44,6 +45,7 @@ clearskies_aws/input_outputs/lambda_input_output.py,sha256=bKzMXmvRajl0RBOGmXASr
|
|
|
44
45
|
clearskies_aws/input_outputs/lambda_invoke.py,sha256=R6QLTSsNkdnxMegq___5oK_BV1u6s76MhdJAO-ifKEU,2854
|
|
45
46
|
clearskies_aws/input_outputs/lambda_sns.py,sha256=Tsm4PtVLfmlZ6SCY0tGpsRlXJ-3ZWTu_Gh7QL6ir9gc,3050
|
|
46
47
|
clearskies_aws/input_outputs/lambda_sqs_standard.py,sha256=RPWO6vvWENAny94KQvKJmF17RtNr-ihTcqnRCz-oq4U,2911
|
|
48
|
+
clearskies_aws/input_outputs/lambda_step_function.py,sha256=CfLy3lwNCGrtNO2b0KOoWwh8zy4gl2Id8Q1ZuLIobKo,8405
|
|
47
49
|
clearskies_aws/mocks/__init__.py,sha256=mn764gINN667tYoJfnsM6HjAAhCsO_kZ6E-fUwdLY50,22
|
|
48
50
|
clearskies_aws/mocks/actions/__init__.py,sha256=to1r8B365Et2PRVfUWWnJGt7Hdr8vwwQuNyZvTSTP6g,152
|
|
49
51
|
clearskies_aws/mocks/actions/ses.py,sha256=KsII9ggU364BQoxgHfO2rxi6tjVsdnwJyMhtclOhWLQ,998
|
|
@@ -62,7 +64,7 @@ clearskies_aws/secrets/additional_configs/iam_db_auth.py,sha256=PwyiLaacpRfhBKzQ
|
|
|
62
64
|
clearskies_aws/secrets/additional_configs/iam_db_auth_with_ssm.py,sha256=ABY29X-YvrE6vvNo6kVdf4DqyRNq5cFR5SfK7MNkltE,3463
|
|
63
65
|
clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py,sha256=mLaplwvJLSbGh6oXgdOKL9Mv-6hLv5OUYCfEwHbHvLE,3700
|
|
64
66
|
clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssm_bastion.py,sha256=2VHOwto4I9gBwrpd2HGpL-Wr0T2S-jFjUhe2Ib8hNJ8,6596
|
|
65
|
-
clear_skies_aws-2.0.
|
|
66
|
-
clear_skies_aws-2.0.
|
|
67
|
-
clear_skies_aws-2.0.
|
|
68
|
-
clear_skies_aws-2.0.
|
|
67
|
+
clear_skies_aws-2.0.12.dist-info/METADATA,sha256=Peh_b5gfQkU62yNOdIGRsPWVpVXsyGS0YeQGKZgcWe8,9077
|
|
68
|
+
clear_skies_aws-2.0.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
69
|
+
clear_skies_aws-2.0.12.dist-info/licenses/LICENSE,sha256=MkEX8JF8kZxdyBpTTcB0YTd-xZpWnHvbRlw-pQh8u58,1069
|
|
70
|
+
clear_skies_aws-2.0.12.dist-info/RECORD,,
|
|
@@ -7,6 +7,12 @@ import clearskies.model
|
|
|
7
7
|
import clearskies.query
|
|
8
8
|
from clearskies.autodoc.schema import Schema as AutoDocSchema
|
|
9
9
|
from clearskies.di import inject
|
|
10
|
+
from clearskies.query.result import (
|
|
11
|
+
CountQueryResult,
|
|
12
|
+
RecordQueryResult,
|
|
13
|
+
RecordsQueryResult,
|
|
14
|
+
SuccessQueryResult,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
from clearskies_aws.di.inject import boto3
|
|
12
18
|
|
|
@@ -31,35 +37,25 @@ class Backend(clearskies.Configurable, clearskies.backends.Backend, clearskies.d
|
|
|
31
37
|
boto3 = boto3.Boto3()
|
|
32
38
|
environment = inject.Environment()
|
|
33
39
|
|
|
34
|
-
def update(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) ->
|
|
40
|
+
def update(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> RecordQueryResult:
|
|
35
41
|
"""Update the record with the given id with the information from the data dictionary."""
|
|
36
|
-
return {}
|
|
42
|
+
return RecordQueryResult(record={})
|
|
37
43
|
|
|
38
|
-
def create(self, data: dict[str, Any], model: clearskies.model.Model) ->
|
|
44
|
+
def create(self, data: dict[str, Any], model: clearskies.model.Model) -> RecordQueryResult:
|
|
39
45
|
"""Create a record with the information from the data dictionary."""
|
|
40
|
-
return {}
|
|
46
|
+
return RecordQueryResult(record={})
|
|
41
47
|
|
|
42
|
-
def delete(self, id: int | str, model: clearskies.model.Model) ->
|
|
48
|
+
def delete(self, id: int | str, model: clearskies.model.Model) -> SuccessQueryResult:
|
|
43
49
|
"""Delete the record with the given id."""
|
|
44
|
-
return
|
|
50
|
+
return SuccessQueryResult()
|
|
45
51
|
|
|
46
|
-
def count(self, query: clearskies.query.Query) ->
|
|
52
|
+
def count(self, query: clearskies.query.Query) -> CountQueryResult:
|
|
47
53
|
"""Return the number of records which match the given query configuration."""
|
|
48
|
-
return 1
|
|
49
|
-
|
|
50
|
-
def records(
|
|
51
|
-
self,
|
|
52
|
-
query: clearskies.query.Query,
|
|
53
|
-
next_page_data: dict[str, str | int] | None = None,
|
|
54
|
-
) -> list[dict[str, Any]]:
|
|
55
|
-
"""
|
|
56
|
-
Return a list of records that match the given query configuration.
|
|
54
|
+
return CountQueryResult(count=1)
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"""
|
|
62
|
-
return []
|
|
56
|
+
def records(self, query: clearskies.query.Query) -> RecordsQueryResult:
|
|
57
|
+
"""Return a list of records that match the given query configuration."""
|
|
58
|
+
return RecordsQueryResult(records=[])
|
|
63
59
|
|
|
64
60
|
def validate_pagination_data(self, data: dict[str, Any], case_mapping: Callable[[str], str]) -> str:
|
|
65
61
|
"""
|
|
@@ -5,6 +5,7 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
from clearskies import Model
|
|
7
7
|
from clearskies.query import Query
|
|
8
|
+
from clearskies.query.result import CountQueryResult, RecordQueryResult, RecordsQueryResult, SuccessQueryResult
|
|
8
9
|
from types_boto3_sqs import SQSClient
|
|
9
10
|
|
|
10
11
|
from clearskies_aws.backends import backend
|
|
@@ -37,25 +38,21 @@ class SqsBackend(backend.Backend):
|
|
|
37
38
|
|
|
38
39
|
return self._sqs
|
|
39
40
|
|
|
40
|
-
def create(self, data: dict[str, Any], model: Model) ->
|
|
41
|
+
def create(self, data: dict[str, Any], model: Model) -> RecordQueryResult:
|
|
41
42
|
self.sqs.send_message(
|
|
42
43
|
QueueUrl=model.destination_name(),
|
|
43
44
|
MessageBody=json.dumps(data),
|
|
44
45
|
)
|
|
45
|
-
return {**data}
|
|
46
|
+
return RecordQueryResult(record={**data})
|
|
46
47
|
|
|
47
|
-
def update(self, id: int | str, data: dict[str, Any], model: Model) ->
|
|
48
|
+
def update(self, id: int | str, data: dict[str, Any], model: Model) -> RecordQueryResult:
|
|
48
49
|
raise ValueError("The SQS backend only supports the create operation")
|
|
49
50
|
|
|
50
|
-
def delete(self, id: int | str, model: Model) ->
|
|
51
|
+
def delete(self, id: int | str, model: Model) -> SuccessQueryResult:
|
|
51
52
|
raise ValueError("The SQS backend only supports the create operation")
|
|
52
53
|
|
|
53
|
-
def count(self, query: Query) ->
|
|
54
|
+
def count(self, query: Query) -> CountQueryResult:
|
|
54
55
|
raise ValueError("The SQS backend only supports the create operation")
|
|
55
56
|
|
|
56
|
-
def records(
|
|
57
|
-
self,
|
|
58
|
-
query: Query,
|
|
59
|
-
next_page_data: dict[str, str | int] | None = None,
|
|
60
|
-
) -> list[dict[str, Any]]:
|
|
57
|
+
def records(self, query: Query) -> RecordsQueryResult:
|
|
61
58
|
raise ValueError("The SQS backend only supports the create operation")
|
|
@@ -11,6 +11,7 @@ from clearskies_aws.contexts.lambda_sns import LambdaSns
|
|
|
11
11
|
from clearskies_aws.contexts.lambda_sqs_standard import (
|
|
12
12
|
LambdaSqsStandard,
|
|
13
13
|
)
|
|
14
|
+
from clearskies_aws.contexts.lambda_step_function import LambdaStepFunction
|
|
14
15
|
|
|
15
16
|
__all__ = [
|
|
16
17
|
"CliWebSocketMock",
|
|
@@ -20,4 +21,5 @@ __all__ = [
|
|
|
20
21
|
"LambdaInvoke",
|
|
21
22
|
"LambdaSns",
|
|
22
23
|
"LambdaSqsStandard",
|
|
24
|
+
"LambdaStepFunction",
|
|
23
25
|
]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
from clearskies.contexts.context import Context
|
|
6
|
+
|
|
7
|
+
from clearskies_aws.input_outputs.lambda_step_function import LambdaStepFunction as LambdaStepFunctionInputOutput
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LambdaStepFunction(Context):
|
|
11
|
+
"""
|
|
12
|
+
Execute a Lambda invoked from AWS Step Functions.
|
|
13
|
+
|
|
14
|
+
This context is used when your clearskies application is running in a Lambda that is
|
|
15
|
+
invoked from an AWS Step Functions state machine. It supports extracting variables
|
|
16
|
+
assigned in the Step Functions state and exposing them through the clearskies
|
|
17
|
+
Environment class.
|
|
18
|
+
|
|
19
|
+
### Usage
|
|
20
|
+
|
|
21
|
+
Basic usage:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import clearskies
|
|
25
|
+
import clearskies_aws
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def my_function(request_data, environment):
|
|
29
|
+
# Access extracted environment variables
|
|
30
|
+
business_name = environment.get("BUSINESS_NAME")
|
|
31
|
+
return {"business": business_name, "data": request_data}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
lambda_step_function = clearskies_aws.contexts.LambdaStepFunction(
|
|
35
|
+
clearskies.endpoints.Callable(
|
|
36
|
+
my_function,
|
|
37
|
+
return_standard_response=False,
|
|
38
|
+
),
|
|
39
|
+
environment_keys=["BUSINESS_NAME", "GITLAB_AUTH_KEY"],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def lambda_handler(event, context):
|
|
44
|
+
return lambda_step_function(event, context)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Configuration Options
|
|
48
|
+
|
|
49
|
+
The `environment_keys` parameter accepts three types:
|
|
50
|
+
|
|
51
|
+
**1. List of keys** - Extract specific keys from the event:
|
|
52
|
+
```python
|
|
53
|
+
clearskies_aws.contexts.LambdaStepFunction(
|
|
54
|
+
endpoint, environment_keys=["BUSINESS_NAME", "GITLAB_AUTH_KEY"]
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**2. Mapping dict** - Map event keys to environment variable names:
|
|
59
|
+
```python
|
|
60
|
+
clearskies_aws.contexts.LambdaStepFunction(
|
|
61
|
+
endpoint,
|
|
62
|
+
environment_keys={
|
|
63
|
+
"BUSINESS_NAME": "BUSINESS_NAME", # same name
|
|
64
|
+
"GITLAB_AUTH_KEY": "GITLAB_TOKEN_PATH", # rename
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**3. Callable** - Full control via a function (supports dependency injection):
|
|
70
|
+
```python
|
|
71
|
+
def extract_env_vars(event, secrets):
|
|
72
|
+
# Can inject clearskies dependencies like secrets
|
|
73
|
+
return {
|
|
74
|
+
"BUSINESS_NAME": event.get("BUSINESS_NAME"),
|
|
75
|
+
"GITLAB_KEY": secrets.get(event.get("GITLAB_AUTH_KEY")),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
clearskies_aws.contexts.LambdaStepFunction(endpoint, environment_keys=extract_env_vars)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Context Specifics
|
|
83
|
+
|
|
84
|
+
When using this context, the following named parameters become available to inject
|
|
85
|
+
into any callable invoked by clearskies:
|
|
86
|
+
|
|
87
|
+
| Name | Type | Description |
|
|
88
|
+
|:----------------------:|:------------------:|:------------------------------------:|
|
|
89
|
+
| `event` | `dict[str, Any]` | The lambda `event` object |
|
|
90
|
+
| `context` | `dict[str, Any]` | The lambda `context` object |
|
|
91
|
+
| `invocation_type` | `str` | Always `"step-functions"` |
|
|
92
|
+
| `function_name` | `str` | The name of the lambda function |
|
|
93
|
+
| `function_version` | `str` | The function version |
|
|
94
|
+
| `request_id` | `str` | The AWS request id for the call |
|
|
95
|
+
| `states_context` | `dict[str, Any]` | The Step Functions $states context |
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
endpoint,
|
|
102
|
+
environment_keys: list[str] | dict[str, str] | Callable[..., dict[str, Any]] | None = None,
|
|
103
|
+
**kwargs,
|
|
104
|
+
):
|
|
105
|
+
super().__init__(endpoint, **kwargs)
|
|
106
|
+
self._environment_keys = environment_keys
|
|
107
|
+
|
|
108
|
+
def __call__( # type: ignore[override]
|
|
109
|
+
self,
|
|
110
|
+
event: dict[str, Any],
|
|
111
|
+
context: dict[str, Any],
|
|
112
|
+
request_method: str = "",
|
|
113
|
+
url: str = "",
|
|
114
|
+
) -> dict[str, Any]:
|
|
115
|
+
input_output = LambdaStepFunctionInputOutput(
|
|
116
|
+
event,
|
|
117
|
+
context,
|
|
118
|
+
request_method=request_method,
|
|
119
|
+
url=url,
|
|
120
|
+
environment_keys=self._environment_keys,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Inject extra environment variables from the step function event
|
|
124
|
+
input_output.inject_extra_environment_variables(
|
|
125
|
+
self.di.build("environment", cache=True),
|
|
126
|
+
self.di,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return self.execute_application(input_output)
|
|
@@ -9,6 +9,7 @@ from clearskies_aws.input_outputs.lambda_api_gateway_web_socket import (
|
|
|
9
9
|
from clearskies_aws.input_outputs.lambda_invoke import LambdaInvoke
|
|
10
10
|
from clearskies_aws.input_outputs.lambda_sns import LambdaSns
|
|
11
11
|
from clearskies_aws.input_outputs.lambda_sqs_standard import LambdaSqsStandard
|
|
12
|
+
from clearskies_aws.input_outputs.lambda_step_function import LambdaStepFunction
|
|
12
13
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"CliWebSocketMock",
|
|
@@ -18,4 +19,5 @@ __all__ = [
|
|
|
18
19
|
"LambdaInvoke",
|
|
19
20
|
"LambdaSns",
|
|
20
21
|
"LambdaSqsStandard",
|
|
22
|
+
"LambdaStepFunction",
|
|
21
23
|
]
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any as TypingAny
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from clearskies.configs import Any
|
|
8
|
+
from clearskies.di import Di
|
|
9
|
+
from clearskies.environment import Environment
|
|
10
|
+
from clearskies.exceptions import ClientError
|
|
11
|
+
from clearskies.input_outputs import Headers
|
|
12
|
+
|
|
13
|
+
from clearskies_aws.input_outputs import lambda_input_output
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LambdaStepFunction(lambda_input_output.LambdaInputOutput):
|
|
17
|
+
"""
|
|
18
|
+
Input/output handler for Lambda invocations from AWS Step Functions.
|
|
19
|
+
|
|
20
|
+
This input/output handles Lambda invocations that come from AWS Step Functions state machines.
|
|
21
|
+
It provides support for extracting variables assigned in the Step Functions state and exposing
|
|
22
|
+
them through the clearskies Environment class.
|
|
23
|
+
|
|
24
|
+
The primary use case is when you want to use Step Functions variables (set via the `Assign` feature
|
|
25
|
+
in JSONata expressions) as environment variables in your clearskies application. This allows you to
|
|
26
|
+
reuse the same Lambda function with different configuration values passed through the state machine.
|
|
27
|
+
|
|
28
|
+
The `environment_keys` parameter controls how variables are extracted from the event and injected
|
|
29
|
+
into the environment. It accepts three types:
|
|
30
|
+
|
|
31
|
+
**List of keys** - Extract specific keys directly from the event:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
LambdaStepFunction(event, context, environment_keys=["BUSINESS_NAME", "API_KEY"])
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Mapping dict** - Map event keys to different environment variable names:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
LambdaStepFunction(
|
|
41
|
+
event,
|
|
42
|
+
context,
|
|
43
|
+
environment_keys={
|
|
44
|
+
"BUSINESS_NAME": "COMPANY_NAME", # event["BUSINESS_NAME"] -> env["COMPANY_NAME"]
|
|
45
|
+
"GITLAB_AUTH_KEY": "GITLAB_TOKEN_PATH",
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Callable** - Full control via a function that can use dependency injection:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
def extract_env_vars(event, secrets):
|
|
54
|
+
# Can inject clearskies dependencies like secrets
|
|
55
|
+
return {
|
|
56
|
+
"BUSINESS_NAME": event.get("BUSINESS_NAME"),
|
|
57
|
+
"GITLAB_KEY": secrets.get(event.get("GITLAB_AUTH_KEY")),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
LambdaStepFunction(event, context, environment_keys=extract_env_vars)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Note that when using a list or dict, all specified keys must exist in the event or a `KeyError`
|
|
65
|
+
will be raised. When using a callable, it must return a dictionary or a `TypeError` will be raised.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
Configuration for extracting environment variables from the Step Functions event.
|
|
70
|
+
|
|
71
|
+
Can be a list of keys to extract directly, a dict mapping event keys to environment names,
|
|
72
|
+
or a callable that receives the event and returns a dict of environment variables.
|
|
73
|
+
"""
|
|
74
|
+
environment_keys = Any(default=None)
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
event: dict[str, TypingAny],
|
|
79
|
+
context: dict[str, TypingAny],
|
|
80
|
+
request_method: str = "",
|
|
81
|
+
url: str = "",
|
|
82
|
+
environment_keys: list[str] | dict[str, str] | Callable[..., dict[str, TypingAny]] | None = None,
|
|
83
|
+
):
|
|
84
|
+
super().__init__(event, context)
|
|
85
|
+
|
|
86
|
+
self.environment_keys = environment_keys
|
|
87
|
+
|
|
88
|
+
if url:
|
|
89
|
+
self.url = url
|
|
90
|
+
self.path = url
|
|
91
|
+
else:
|
|
92
|
+
self.supports_url = True
|
|
93
|
+
if request_method:
|
|
94
|
+
self.request_method = request_method.upper()
|
|
95
|
+
else:
|
|
96
|
+
self.supports_request_method = False
|
|
97
|
+
|
|
98
|
+
self.request_headers = Headers({})
|
|
99
|
+
|
|
100
|
+
def inject_extra_environment_variables(self, environment: Environment, di: Di) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Extract and inject environment variables from the event into the Environment.
|
|
103
|
+
|
|
104
|
+
This method is called by the context to extract variables from the Step Functions event
|
|
105
|
+
and inject them into the clearskies Environment. The extraction behavior depends on the
|
|
106
|
+
type of `environment_keys`:
|
|
107
|
+
|
|
108
|
+
- If `environment_keys` is `None`, no extraction is performed.
|
|
109
|
+
- If `environment_keys` is a list, each key is extracted from the event and injected
|
|
110
|
+
with the same name.
|
|
111
|
+
- If `environment_keys` is a dict, each event key is mapped to the corresponding
|
|
112
|
+
environment name.
|
|
113
|
+
- If `environment_keys` is a callable, it is invoked via the DI container (allowing
|
|
114
|
+
dependency injection) and must return a dict of environment variables.
|
|
115
|
+
|
|
116
|
+
Raises `KeyError` if a requested key is not found in the event (for list/dict modes).
|
|
117
|
+
Raises `TypeError` if a callable does not return a dictionary.
|
|
118
|
+
"""
|
|
119
|
+
if self.environment_keys is None:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
extracted: dict[str, TypingAny] = {}
|
|
123
|
+
|
|
124
|
+
if callable(self.environment_keys):
|
|
125
|
+
# Let clearskies call the callable so it can inject dependencies
|
|
126
|
+
result = di.call_function(self.environment_keys, event=self.event)
|
|
127
|
+
if not isinstance(result, dict):
|
|
128
|
+
callable_name = getattr(self.environment_keys, "__name__", str(self.environment_keys))
|
|
129
|
+
raise TypeError(
|
|
130
|
+
f"The environment_keys callable '{callable_name}' must return a dictionary, "
|
|
131
|
+
f"but returned {type(result).__name__}"
|
|
132
|
+
)
|
|
133
|
+
extracted = result
|
|
134
|
+
elif isinstance(self.environment_keys, dict):
|
|
135
|
+
for event_key, env_name in self.environment_keys.items():
|
|
136
|
+
if event_key not in self.event:
|
|
137
|
+
raise KeyError(
|
|
138
|
+
f"environment_keys requested a key called `{event_key}` but this was not found in the event"
|
|
139
|
+
)
|
|
140
|
+
extracted[env_name] = self.event[event_key]
|
|
141
|
+
elif isinstance(self.environment_keys, list):
|
|
142
|
+
for key in self.environment_keys:
|
|
143
|
+
if key not in self.event:
|
|
144
|
+
raise KeyError(
|
|
145
|
+
f"environment_keys requested a key called `{key}` but this was not found in the event"
|
|
146
|
+
)
|
|
147
|
+
extracted[key] = self.event[key]
|
|
148
|
+
|
|
149
|
+
# Inject extracted values into the environment
|
|
150
|
+
for key, value in extracted.items():
|
|
151
|
+
environment.set(key, value)
|
|
152
|
+
|
|
153
|
+
def has_body(self) -> bool:
|
|
154
|
+
"""Step Functions invocations always have a body (the event itself)."""
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
def get_body(self) -> str:
|
|
158
|
+
"""Return the entire event as a JSON-encoded string."""
|
|
159
|
+
if isinstance(self.event, (dict, list)):
|
|
160
|
+
return json.dumps(self.event)
|
|
161
|
+
return str(self.event)
|
|
162
|
+
|
|
163
|
+
def respond(self, body: TypingAny, status_code: int = 200) -> TypingAny:
|
|
164
|
+
"""Return the response directly for Step Functions invocations (no HTTP wrapping)."""
|
|
165
|
+
if isinstance(body, bytes):
|
|
166
|
+
return body.decode("utf-8")
|
|
167
|
+
return body
|
|
168
|
+
|
|
169
|
+
def get_protocol(self) -> str:
|
|
170
|
+
"""Return the protocol identifier for Step Functions invocations."""
|
|
171
|
+
return "step-functions"
|
|
172
|
+
|
|
173
|
+
def context_specifics(self) -> dict[str, TypingAny]:
|
|
174
|
+
"""
|
|
175
|
+
Provide Step Functions specific context data for dependency injection.
|
|
176
|
+
|
|
177
|
+
Returns a dict containing:
|
|
178
|
+
- `event`: The raw Lambda event
|
|
179
|
+
- `context`: The raw Lambda context
|
|
180
|
+
- `invocation_type`: Always "step-functions"
|
|
181
|
+
- `function_name`: The Lambda function name
|
|
182
|
+
- `function_version`: The Lambda function version
|
|
183
|
+
- `request_id`: The AWS request ID
|
|
184
|
+
- `states_context`: The Step Functions $states context (if present)
|
|
185
|
+
"""
|
|
186
|
+
states_context = self.event.get("$states", {})
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
**super().context_specifics(),
|
|
190
|
+
"invocation_type": "step-functions",
|
|
191
|
+
"function_name": self.context.get("function_name"),
|
|
192
|
+
"function_version": self.context.get("function_version"),
|
|
193
|
+
"request_id": self.context.get("aws_request_id"),
|
|
194
|
+
"states_context": states_context,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def request_data(self) -> dict[str, TypingAny] | list[TypingAny] | None:
|
|
199
|
+
"""Return the event directly as request data."""
|
|
200
|
+
return self.event
|
|
201
|
+
|
|
202
|
+
def json_body(
|
|
203
|
+
self, required: bool = True, allow_non_json_bodies: bool = False
|
|
204
|
+
) -> dict[str, TypingAny] | list[TypingAny] | None:
|
|
205
|
+
"""Return the event as JSON data (already parsed from the Lambda invocation)."""
|
|
206
|
+
if required and not self.event:
|
|
207
|
+
raise ClientError("Request body was not valid JSON")
|
|
208
|
+
return self.event
|
|
File without changes
|
|
File without changes
|