clear-skies-aws 2.0.11__py3-none-any.whl → 2.0.13__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clear-skies-aws
3
- Version: 2.0.11
3
+ Version: 2.0.13
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
20
+ Requires-Dist: clear-skies<3.0.0,>=2.0.39
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=WpsoT0pZOdilvtOkbiNtk6DoOEzmjyWcCDfUCWOlToc,4316
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=kHTzgBwpYzV31UcGaoSUcC_7eZEwm-GsCHvXFM1OLT0,2152
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=kBLUlkpIp88n5RgbE9gVi4rDnJou81vCE5g_aiv2v8Y,717
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
@@ -28,14 +29,14 @@ clearskies_aws/cursors/port_forwarding/__init__.py,sha256=LBcFYeIIfmGhxf3Ezn1KCh
28
29
  clearskies_aws/cursors/port_forwarding/ssm.py,sha256=2tEznKflMG5a8g1AzHWghHkMp4BdBFhewB8WigFR5F4,4878
29
30
  clearskies_aws/di/__init__.py,sha256=pLHSIKxS1oELOgttRuwM0yXdJRxjZKXQ6tPxme2db0U,222
30
31
  clearskies_aws/di/aws_additional_config_auto_import.py,sha256=94h_YsPBcdwMhqn0VAAfId1jLL5vCsk76kUrr-6ET_U,1275
31
- clearskies_aws/di/inject/__init__.py,sha256=5_x5_BBQwC6J4k5YLdTm1DfIDM-95zXz1L5a1nMrlrY,186
32
+ clearskies_aws/di/inject/__init__.py,sha256=uuQlXuGgVH1rwMeQtIEQS9NiVjSHm4H4WkctWIakPS8,272
32
33
  clearskies_aws/di/inject/boto3.py,sha256=yUDiEpR2Si6pKcLrqMOlQEUU0pi6MS1tXNdoyC2mjwk,408
33
34
  clearskies_aws/di/inject/boto3_session.py,sha256=11UYHz5kgrrx5lawoYaOFBm-QIoa45YUCMAOn4gT8Jo,383
34
35
  clearskies_aws/di/inject/parameter_store.py,sha256=g0uAVwQEywLO9bCcYLbDKuyYnYgVyrfcYsOBJWYGads,475
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=RTDFwhPWZ2S0tZQiIPH0Tkj2xF-9qBjZte_CA2cmGt8,743
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
@@ -52,17 +54,13 @@ clearskies_aws/mocks/actions/sqs.py,sha256=y0Vq7IMbjlfT5JMNHfbPsq9XVZhUF-G0kdXzQ
52
54
  clearskies_aws/mocks/actions/step_function.py,sha256=ENEVy8Ai3vPymbQre5aWa5z2McBjlnopfsLxdO7oEbc,937
53
55
  clearskies_aws/models/__init__.py,sha256=tAU5cPGRSzSClNVRCBxzwlBq6eZO8fftuI3bG1jEyVQ,87
54
56
  clearskies_aws/models/web_socket_connection_model.py,sha256=5M1qfQHKuWMYPUDkwT48QPo2ROey7koizvWLfapsfow,7492
55
- clearskies_aws/secrets/__init__.py,sha256=0mqYja2ETBHJh4b3jgRhhJV1uGdZc9S7cUvcV5QByPs,445
56
- clearskies_aws/secrets/akeyless_with_ssm_cache.py,sha256=32HUS1KQ5F6Fu70HDtojqDL7VZvP_YDgbWLTTmNJvPA,2073
57
- clearskies_aws/secrets/parameter_store.py,sha256=nMpkbUnxHGcCoMD5T-weCS13f1RZA7ZHl24O7qXaZLE,1882
58
- clearskies_aws/secrets/secrets.py,sha256=aDMPj-tuXdRhh8YKMnsJe9V_VLrD8Cru-xUKs8cyDIY,485
59
- clearskies_aws/secrets/secrets_manager.py,sha256=hK10lVmEFJltTv7h2AkYR3ySCVXx1JVy56jWU1hyYso,3708
60
- clearskies_aws/secrets/additional_configs/__init__.py,sha256=0NFOMVod8tte_K0Jq1Qf7_DDBvp6aEE4wF4hddQaW8w,1927
61
- clearskies_aws/secrets/additional_configs/iam_db_auth.py,sha256=PwyiLaacpRfhBKzQBdvGWHUYf5Ymth1sG2ly7Z6RoR0,1238
62
- clearskies_aws/secrets/additional_configs/iam_db_auth_with_ssm.py,sha256=ABY29X-YvrE6vvNo6kVdf4DqyRNq5cFR5SfK7MNkltE,3463
63
- clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py,sha256=mLaplwvJLSbGh6oXgdOKL9Mv-6hLv5OUYCfEwHbHvLE,3700
64
- clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssm_bastion.py,sha256=2VHOwto4I9gBwrpd2HGpL-Wr0T2S-jFjUhe2Ib8hNJ8,6596
65
- clear_skies_aws-2.0.11.dist-info/METADATA,sha256=sHHGa57BEMydO5oT9JSeqZPffwvdk3v7hSgXdeIBqu0,9077
66
- clear_skies_aws-2.0.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
67
- clear_skies_aws-2.0.11.dist-info/licenses/LICENSE,sha256=MkEX8JF8kZxdyBpTTcB0YTd-xZpWnHvbRlw-pQh8u58,1069
68
- clear_skies_aws-2.0.11.dist-info/RECORD,,
57
+ clearskies_aws/secrets/__init__.py,sha256=5QWfe6IyHdAyfOJVZJ52qM5hTkw1siMJ6q6YW95-Jl8,345
58
+ clearskies_aws/secrets/parameter_store.py,sha256=abeG72LdOdx1WxZpjph2b3T9E7ClXqAYqQ9mxoHI2VQ,6565
59
+ clearskies_aws/secrets/secrets.py,sha256=P30Yx0pkxjPnwCNw8ixoUu--4B7EEsg6gpOlazrk4Oc,956
60
+ clearskies_aws/secrets/secrets_manager.py,sha256=VhqKw4W65y3RRoFt9Ws5g7Q8nv2ACqkDYwxwgRpU-sk,7026
61
+ clearskies_aws/secrets/cache_storage/__init__.py,sha256=A6_rUn95NQjJu_VDDNQ1mDDNye18QYGGhXM66orGnb8,255
62
+ clearskies_aws/secrets/cache_storage/parameter_store_cache.py,sha256=k2FVmFepZgssvsGuiAj0t3M56K5ZIVqmsRfTLlQFDWk,4028
63
+ clear_skies_aws-2.0.13.dist-info/METADATA,sha256=JOWiWVeM4tWzx2NeijR9B9Ihv3Zakh5BvrewvhyzpEU,9077
64
+ clear_skies_aws-2.0.13.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
65
+ clear_skies_aws-2.0.13.dist-info/licenses/LICENSE,sha256=MkEX8JF8kZxdyBpTTcB0YTd-xZpWnHvbRlw-pQh8u58,1069
66
+ clear_skies_aws-2.0.13.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) -> dict[str, Any]:
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) -> dict[str, Any]:
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) -> bool:
48
+ def delete(self, id: int | str, model: clearskies.model.Model) -> SuccessQueryResult:
43
49
  """Delete the record with the given id."""
44
- return True
50
+ return SuccessQueryResult()
45
51
 
46
- def count(self, query: clearskies.query.Query) -> int:
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
- next_page_data is used to return data to the caller. Pass in an empty dictionary, and it will be populated
59
- with the data needed to return the next page of results. If it is still an empty dictionary when returned,
60
- then there is no additional data.
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) -> dict[str, Any]:
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) -> dict[str, Any]:
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) -> bool:
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) -> int:
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)
@@ -2,5 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from clearskies_aws.di.inject.boto3 import Boto3
4
4
  from clearskies_aws.di.inject.boto3_session import Boto3Session
5
+ from clearskies_aws.di.inject.parameter_store import ParameterStore
5
6
 
6
- __all__ = ["Boto3", "Boto3Session"]
7
+ __all__ = ["Boto3", "Boto3Session", "ParameterStore"]
@@ -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
@@ -1,5 +1,6 @@
1
- from clearskies_aws.secrets import additional_configs
2
- from clearskies_aws.secrets.akeyless_with_ssm_cache import AkeylessWithSsmCache
1
+ import importlib
2
+
3
+ from clearskies_aws.secrets import cache_storage
3
4
  from clearskies_aws.secrets.parameter_store import ParameterStore
4
5
  from clearskies_aws.secrets.secrets import Secrets
5
6
  from clearskies_aws.secrets.secrets_manager import SecretsManager
@@ -8,6 +9,5 @@ __all__ = [
8
9
  "Secrets",
9
10
  "ParameterStore",
10
11
  "SecretsManager",
11
- "AkeylessWithSsmCache",
12
- "additional_configs",
12
+ "cache_storage",
13
13
  ]
@@ -0,0 +1,9 @@
1
+ """
2
+ AWS-specific secret cache implementations.
3
+
4
+ This module provides cache storage backends for secrets using AWS services.
5
+ """
6
+
7
+ from clearskies_aws.secrets.cache_storage.parameter_store_cache import ParameterStoreCache
8
+
9
+ __all__ = ["ParameterStoreCache"]