Skip to content

Commit 3e313bb

Browse files
fix(event_sources): Update CodePipeline event source to include optional encryption_key field and make user_parameters field optional (#2113)
Co-authored-by: Neil Ramsay <nramsay@cybercx.com> Co-authored-by: Leandro Damascena <leandro.damascena@gmail.com>
1 parent e5f538e commit 3e313bb

File tree

4 files changed

+148
-6
lines changed

4 files changed

+148
-6
lines changed

aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ def function_name(self) -> str:
1616
return self["FunctionName"]
1717

1818
@property
19-
def user_parameters(self) -> str:
19+
def user_parameters(self) -> Optional[str]:
2020
"""User parameters"""
21-
return self["UserParameters"]
21+
return self.get("UserParameters", None)
2222

2323
@property
24-
def decoded_user_parameters(self) -> Dict[str, Any]:
24+
def decoded_user_parameters(self) -> Optional[Dict[str, Any]]:
2525
"""Json Decoded user parameters"""
26-
if self._json_data is None:
26+
if self._json_data is None and self.user_parameters is not None:
2727
self._json_data = json.loads(self.user_parameters)
2828
return self._json_data
2929

@@ -97,6 +97,16 @@ def expiration_time(self) -> Optional[int]:
9797
return self.get("expirationTime")
9898

9999

100+
class CodePipelineEncryptionKey(DictWrapper):
101+
@property
102+
def get_id(self) -> str:
103+
return self["id"]
104+
105+
@property
106+
def get_type(self) -> str:
107+
return self["type"]
108+
109+
100110
class CodePipelineData(DictWrapper):
101111
"""CodePipeline Job Data"""
102112

@@ -125,6 +135,12 @@ def continuation_token(self) -> Optional[str]:
125135
"""A continuation token if continuing job"""
126136
return self.get("continuationToken")
127137

138+
@property
139+
def encryption_key(self) -> Optional[CodePipelineEncryptionKey]:
140+
"""Represents a CodePipeline encryption key"""
141+
key_data = self.get("encryptionKey")
142+
return CodePipelineEncryptionKey(key_data) if key_data is not None else None
143+
128144

129145
class CodePipelineJobEvent(DictWrapper):
130146
"""AWS CodePipeline Job Event
@@ -155,12 +171,12 @@ def data(self) -> CodePipelineData:
155171
return CodePipelineData(self._job["data"])
156172

157173
@property
158-
def user_parameters(self) -> str:
174+
def user_parameters(self) -> Optional[str]:
159175
"""Action configuration user parameters"""
160176
return self.data.action_configuration.configuration.user_parameters
161177

162178
@property
163-
def decoded_user_parameters(self) -> Dict[str, Any]:
179+
def decoded_user_parameters(self) -> Optional[Dict[str, Any]]:
164180
"""Json Decoded action configuration user parameters"""
165181
return self.data.action_configuration.configuration.decoded_user_parameters
166182

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"CodePipeline.job": {
3+
"id": "11111111-abcd-1111-abcd-111111abcdef",
4+
"accountId": "111111111111",
5+
"data": {
6+
"actionConfiguration": {
7+
"configuration": {
8+
"FunctionName": "MyLambdaFunctionForAWSCodePipeline"
9+
}
10+
},
11+
"inputArtifacts": [
12+
{
13+
"name": "ArtifactName",
14+
"revision": null,
15+
"location": {
16+
"type": "S3",
17+
"s3Location": {
18+
"bucketName": "the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890",
19+
"objectKey": "the name of the application, for example CodePipelineDemoApplication.zip"
20+
}
21+
}
22+
}
23+
],
24+
"outputArtifacts": [],
25+
"artifactCredentials": {
26+
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
27+
"secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
28+
"sessionToken": "MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE="
29+
}
30+
}
31+
}
32+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"CodePipeline.job": {
3+
"id": "11111111-abcd-1111-abcd-111111abcdef",
4+
"accountId": "111111111111",
5+
"data": {
6+
"actionConfiguration": {
7+
"configuration": {
8+
"FunctionName": "MyLambdaFunctionForAWSCodePipeline",
9+
"UserParameters": "some-input-such-as-a-URL"
10+
}
11+
},
12+
"inputArtifacts": [
13+
{
14+
"name": "ArtifactName",
15+
"revision": null,
16+
"location": {
17+
"type": "S3",
18+
"s3Location": {
19+
"bucketName": "the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890",
20+
"objectKey": "the name of the application, for example CodePipelineDemoApplication.zip"
21+
}
22+
}
23+
}
24+
],
25+
"outputArtifacts": [],
26+
"artifactCredentials": {
27+
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
28+
"secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
29+
"sessionToken": "MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE="
30+
},
31+
"continuationToken": "A continuation token if continuing job",
32+
"encryptionKey": {
33+
"id": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
34+
"type": "KMS"
35+
}
36+
}
37+
}
38+
}

tests/functional/test_data_classes.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,62 @@ def test_code_pipeline_event():
15441544
assert artifact_credentials_dict["sessionToken"] == artifact_credentials.session_token
15451545

15461546

1547+
def test_code_pipeline_event_with_encryption_keys():
1548+
event = CodePipelineJobEvent(load_event("codePipelineEventWithEncryptionKey.json"))
1549+
1550+
job = event["CodePipeline.job"]
1551+
assert job["id"] == event.get_id
1552+
assert job["accountId"] == event.account_id
1553+
1554+
data = event.data
1555+
assert isinstance(data, CodePipelineData)
1556+
assert job["data"]["continuationToken"] == data.continuation_token
1557+
configuration = data.action_configuration.configuration
1558+
assert "MyLambdaFunctionForAWSCodePipeline" == configuration.function_name
1559+
assert event.user_parameters == configuration.user_parameters
1560+
assert "some-input-such-as-a-URL" == configuration.user_parameters
1561+
1562+
input_artifacts = data.input_artifacts
1563+
assert len(input_artifacts) == 1
1564+
assert "ArtifactName" == input_artifacts[0].name
1565+
assert input_artifacts[0].revision is None
1566+
assert "S3" == input_artifacts[0].location.get_type
1567+
1568+
output_artifacts = data.output_artifacts
1569+
assert len(output_artifacts) == 0
1570+
1571+
artifact_credentials = data.artifact_credentials
1572+
artifact_credentials_dict = event["CodePipeline.job"]["data"]["artifactCredentials"]
1573+
assert artifact_credentials_dict["accessKeyId"] == artifact_credentials.access_key_id
1574+
assert artifact_credentials_dict["secretAccessKey"] == artifact_credentials.secret_access_key
1575+
assert artifact_credentials_dict["sessionToken"] == artifact_credentials.session_token
1576+
1577+
encryption_key = data.encryption_key
1578+
assert "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" == encryption_key.get_id
1579+
assert "KMS" == encryption_key.get_type
1580+
1581+
1582+
def test_code_pipeline_event_missing_user_parameters():
1583+
event = CodePipelineJobEvent(load_event("codePipelineEventEmptyUserParameters.json"))
1584+
1585+
assert event.data.continuation_token is None
1586+
configuration = event.data.action_configuration.configuration
1587+
decoded_params = configuration.decoded_user_parameters
1588+
assert decoded_params == event.decoded_user_parameters
1589+
assert decoded_params is None
1590+
assert configuration.decoded_user_parameters is None
1591+
1592+
1593+
def test_code_pipeline_event_non_json_user_parameters():
1594+
event = CodePipelineJobEvent(load_event("codePipelineEvent.json"))
1595+
1596+
configuration = event.data.action_configuration.configuration
1597+
assert configuration.user_parameters is not None
1598+
1599+
with pytest.raises(json.decoder.JSONDecodeError):
1600+
configuration.decoded_user_parameters
1601+
1602+
15471603
def test_code_pipeline_event_decoded_data():
15481604
event = CodePipelineJobEvent(load_event("codePipelineEventData.json"))
15491605

0 commit comments

Comments
 (0)