From 7dd03a8365bb5a5650d2b198f87b8bfbe4e81476 Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Wed, 12 Apr 2023 13:25:39 +1200 Subject: [PATCH 1/4] test: CodePipeline Event with missing User Params The AWS CodePipeline Lambda Action does not require UserParameters to be set, and therefore will be missing from `action_configuration`. This test ensures the data class returns None. https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-Lambda.html --- .../codePipelineEventEmptyUserParameters.json | 32 +++++++++++++++++++ tests/functional/test_data_classes.py | 11 +++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/events/codePipelineEventEmptyUserParameters.json diff --git a/tests/events/codePipelineEventEmptyUserParameters.json b/tests/events/codePipelineEventEmptyUserParameters.json new file mode 100644 index 00000000000..1a0dec6a15e --- /dev/null +++ b/tests/events/codePipelineEventEmptyUserParameters.json @@ -0,0 +1,32 @@ +{ + "CodePipeline.job": { + "id": "11111111-abcd-1111-abcd-111111abcdef", + "accountId": "111111111111", + "data": { + "actionConfiguration": { + "configuration": { + "FunctionName": "MyLambdaFunctionForAWSCodePipeline" + } + }, + "inputArtifacts": [ + { + "name": "ArtifactName", + "revision": null, + "location": { + "type": "S3", + "s3Location": { + "bucketName": "the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890", + "objectKey": "the name of the application, for example CodePipelineDemoApplication.zip" + } + } + } + ], + "outputArtifacts": [], + "artifactCredentials": { + "accessKeyId": "AKIAIOSFODNN7EXAMPLE", + "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "sessionToken": "MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE=" + } + } + } +} diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index 916e9b61e0d..365ec9de2ef 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -1544,6 +1544,17 @@ def test_code_pipeline_event(): assert artifact_credentials_dict["sessionToken"] == artifact_credentials.session_token +def test_code_pipeline_event_missing_user_parameters(): + event = CodePipelineJobEvent(load_event("codePipelineEventEmptyUserParameters.json")) + + assert event.data.continuation_token is None + configuration = event.data.action_configuration.configuration + decoded_params = configuration.decoded_user_parameters + assert decoded_params == event.decoded_user_parameters + assert decoded_params is None + assert configuration.decoded_user_parameters is None + + def test_code_pipeline_event_decoded_data(): event = CodePipelineJobEvent(load_event("codePipelineEventData.json")) From 33375f0bb4d1058c122dedea4558ac963d728bc7 Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Wed, 12 Apr 2023 15:46:23 +1200 Subject: [PATCH 2/4] test: CodePipeline Event with non-JSON User Params The User Parameters supplied can be arbitary text. This test ensures a JSON parse exception is raised if plain text is supplied as a User Parameter. --- tests/functional/test_data_classes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index 365ec9de2ef..b4f465f838b 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -1555,6 +1555,16 @@ def test_code_pipeline_event_missing_user_parameters(): assert configuration.decoded_user_parameters is None +def test_code_pipeline_event_non_json_user_parameters(): + event = CodePipelineJobEvent(load_event("codePipelineEvent.json")) + + configuration = event.data.action_configuration.configuration + assert configuration.user_parameters is not None + + with pytest.raises(json.decoder.JSONDecodeError): + configuration.decoded_user_parameters + + def test_code_pipeline_event_decoded_data(): event = CodePipelineJobEvent(load_event("codePipelineEventData.json")) From 3fbfc4e875e5fc73916e8afaf3004392b86230a7 Mon Sep 17 00:00:00 2001 From: Neil Ramsay Date: Wed, 12 Apr 2023 15:50:44 +1200 Subject: [PATCH 3/4] fix: CodePipeline Event Optional User Parameters The AWS CodePipeline Lambda Action does not require UserParameters to be set, and therefore will be missing from the `action_configuration`. https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-Lambda.html This commit makes user_parameters optional (i.e. return None if the key isn't present). --- .../data_classes/code_pipeline_job_event.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py b/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py index e17bd13807c..89e23ccbcf1 100644 --- a/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py +++ b/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py @@ -16,14 +16,14 @@ def function_name(self) -> str: return self["FunctionName"] @property - def user_parameters(self) -> str: + def user_parameters(self) -> Optional[str]: """User parameters""" - return self["UserParameters"] + return self.get("UserParameters", None) @property - def decoded_user_parameters(self) -> Dict[str, Any]: + def decoded_user_parameters(self) -> Optional[Dict[str, Any]]: """Json Decoded user parameters""" - if self._json_data is None: + if self._json_data is None and self.user_parameters is not None: self._json_data = json.loads(self.user_parameters) return self._json_data @@ -155,12 +155,12 @@ def data(self) -> CodePipelineData: return CodePipelineData(self._job["data"]) @property - def user_parameters(self) -> str: + def user_parameters(self) -> Optional[str]: """Action configuration user parameters""" return self.data.action_configuration.configuration.user_parameters @property - def decoded_user_parameters(self) -> Dict[str, Any]: + def decoded_user_parameters(self) -> Optional[Dict[str, Any]]: """Json Decoded action configuration user parameters""" return self.data.action_configuration.configuration.decoded_user_parameters From 5c0906f923f64883107d93c388650ad8dce5ea56 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 12 Apr 2023 16:26:55 +0100 Subject: [PATCH 4/4] fix(data_classes): adding codebuild encryption key field --- .../data_classes/code_pipeline_job_event.py | 16 ++++++++ .../codePipelineEventWithEncryptionKey.json | 38 +++++++++++++++++++ tests/functional/test_data_classes.py | 35 +++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 tests/events/codePipelineEventWithEncryptionKey.json diff --git a/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py b/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py index 89e23ccbcf1..a4139ebbe68 100644 --- a/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py +++ b/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py @@ -97,6 +97,16 @@ def expiration_time(self) -> Optional[int]: return self.get("expirationTime") +class CodePipelineEncryptionKey(DictWrapper): + @property + def get_id(self) -> str: + return self["id"] + + @property + def get_type(self) -> str: + return self["type"] + + class CodePipelineData(DictWrapper): """CodePipeline Job Data""" @@ -125,6 +135,12 @@ def continuation_token(self) -> Optional[str]: """A continuation token if continuing job""" return self.get("continuationToken") + @property + def encryption_key(self) -> Optional[CodePipelineEncryptionKey]: + """Represents a CodePipeline encryption key""" + key_data = self.get("encryptionKey") + return CodePipelineEncryptionKey(key_data) if key_data is not None else None + class CodePipelineJobEvent(DictWrapper): """AWS CodePipeline Job Event diff --git a/tests/events/codePipelineEventWithEncryptionKey.json b/tests/events/codePipelineEventWithEncryptionKey.json new file mode 100644 index 00000000000..e4a8528e148 --- /dev/null +++ b/tests/events/codePipelineEventWithEncryptionKey.json @@ -0,0 +1,38 @@ +{ + "CodePipeline.job": { + "id": "11111111-abcd-1111-abcd-111111abcdef", + "accountId": "111111111111", + "data": { + "actionConfiguration": { + "configuration": { + "FunctionName": "MyLambdaFunctionForAWSCodePipeline", + "UserParameters": "some-input-such-as-a-URL" + } + }, + "inputArtifacts": [ + { + "name": "ArtifactName", + "revision": null, + "location": { + "type": "S3", + "s3Location": { + "bucketName": "the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890", + "objectKey": "the name of the application, for example CodePipelineDemoApplication.zip" + } + } + } + ], + "outputArtifacts": [], + "artifactCredentials": { + "accessKeyId": "AKIAIOSFODNN7EXAMPLE", + "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "sessionToken": "MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE=" + }, + "continuationToken": "A continuation token if continuing job", + "encryptionKey": { + "id": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "type": "KMS" + } + } + } +} diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index b4f465f838b..5e2aad30e8e 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -1544,6 +1544,41 @@ def test_code_pipeline_event(): assert artifact_credentials_dict["sessionToken"] == artifact_credentials.session_token +def test_code_pipeline_event_with_encryption_keys(): + event = CodePipelineJobEvent(load_event("codePipelineEventWithEncryptionKey.json")) + + job = event["CodePipeline.job"] + assert job["id"] == event.get_id + assert job["accountId"] == event.account_id + + data = event.data + assert isinstance(data, CodePipelineData) + assert job["data"]["continuationToken"] == data.continuation_token + configuration = data.action_configuration.configuration + assert "MyLambdaFunctionForAWSCodePipeline" == configuration.function_name + assert event.user_parameters == configuration.user_parameters + assert "some-input-such-as-a-URL" == configuration.user_parameters + + input_artifacts = data.input_artifacts + assert len(input_artifacts) == 1 + assert "ArtifactName" == input_artifacts[0].name + assert input_artifacts[0].revision is None + assert "S3" == input_artifacts[0].location.get_type + + output_artifacts = data.output_artifacts + assert len(output_artifacts) == 0 + + artifact_credentials = data.artifact_credentials + artifact_credentials_dict = event["CodePipeline.job"]["data"]["artifactCredentials"] + assert artifact_credentials_dict["accessKeyId"] == artifact_credentials.access_key_id + assert artifact_credentials_dict["secretAccessKey"] == artifact_credentials.secret_access_key + assert artifact_credentials_dict["sessionToken"] == artifact_credentials.session_token + + encryption_key = data.encryption_key + assert "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" == encryption_key.get_id + assert "KMS" == encryption_key.get_type + + def test_code_pipeline_event_missing_user_parameters(): event = CodePipelineJobEvent(load_event("codePipelineEventEmptyUserParameters.json"))