From 1abd88e9ec5e9555c494128823b99dbb5ad14382 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 28 Aug 2024 11:41:33 +0000 Subject: [PATCH 1/2] add support for version and deduplication --- README.md | 9 +++++ bin/runDockerUnitTests.sh | 4 +-- dd_import/dd_api.py | 7 ++++ dd_import/environment.py | 4 +++ unittests/test_api.py | 73 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 91 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 466c1ef..31c5fd0 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ All parameters need to be provided as environment variables: | DD_PRODUCT_TYPE_NAME | Mandatory | Mandatory | If a product type with this name does not exist, it will be created | | DD_PRODUCT_NAME | Mandatory | Mandatory | If a product with this name does not exist, it will be created | | DD_ENGAGEMENT_NAME | Mandatory | - | If an engagement with this name does not exist for the given product, it will be created | +| DD_ENGAGEMENT_VERSION | Optional | - | If provided, the version is used as an additional filter to the name to find the matching engagement | +| DD_ENGAGEMENT_DEDUPLICATION | Optional | - | Default: false | | DD_ENGAGEMENT_TARGET_START | Optional | - | Format: YYYY-MM-DD, default: `today`. The target start date for a newly created engagement. | | DD_ENGAGEMENT_TARGET_END | Optional | - | Format: YYYY-MM-DD, default: `2999-12-31`. The target start date for a newly created engagement. | | DD_TEST_NAME | Mandatory | - | If a test with this name does not exist for the given engagement, it will be created | @@ -176,6 +178,13 @@ Another example, showing how to use `dd-import` within a GitHub Action, can be f `./bin/runDockerUnitTests.sh` - First creates the docker image and then starts a docker container in which the unit tests are executed. +### Publish new Image + +``` +docker build -f docker/Dockerfile -t brightercore.azurecr.io/dd-import:latest . +docker push brightercore.azurecr.io/dd-import:latest +``` + ## License Licensed under the [3-Clause BSD License](LICENSE.txt) diff --git a/bin/runDockerUnitTests.sh b/bin/runDockerUnitTests.sh index e1caa1c..c97d67f 100755 --- a/bin/runDockerUnitTests.sh +++ b/bin/runDockerUnitTests.sh @@ -1,7 +1,7 @@ #!/bin/sh -if docker build -f docker/Dockerfile -t dd-import:latest .; then - docker run --rm dd-import:latest ./bin/runUnitTests.sh +if docker build -f docker/Dockerfile -t brightercore.azurecr.io/dd-import:latest .; then + docker run --rm brightercore.azurecr.io/dd-import:latest ./bin/runUnitTests.sh else exit 1 fi diff --git a/dd_import/dd_api.py b/dd_import/dd_api.py index 042932f..04d56d7 100644 --- a/dd_import/dd_api.py +++ b/dd_import/dd_api.py @@ -107,6 +107,9 @@ def new_product(self, product_type): def get_engagement(self, product): payload = {'name': self.environment.engagement_name, 'product': product} + if self.environment.engagement_version is not None: + payload['version'] = self.environment.engagement_version + r = requests.get(self.engagement_url, headers=self.headers, params=payload, @@ -126,7 +129,11 @@ def new_engagement(self, product): 'target_start': self.environment.engagement_target_start, 'target_end': self.environment.engagement_target_end, 'engagement_type': 'CI/CD', + 'deduplication_on_engagement': self.environment.engagement_deduplication, 'status': 'In Progress'} + if self.environment.engagement_version is not None: + payload['version'] = self.environment.engagement_version + if self.environment.source_code_management_uri is not None: payload['source_code_management_uri'] = self.environment.source_code_management_uri r = requests.post(self.engagement_url, diff --git a/dd_import/environment.py b/dd_import/environment.py index d2bc2ce..bb14316 100644 --- a/dd_import/environment.py +++ b/dd_import/environment.py @@ -11,6 +11,8 @@ def __init__(self): self.product_name = os.getenv('DD_PRODUCT_NAME') self.product_type_name = os.getenv('DD_PRODUCT_TYPE_NAME') self.engagement_name = os.getenv('DD_ENGAGEMENT_NAME') + self.engagement_version = os.getenv('DD_ENGAGEMENT_VERSION', None) + self.engagement_deduplication = os.getenv('DD_ENGAGEMENT_DEDUPLICATION', 'False').lower() in ['true'] self.engagement_target_start = os.getenv('DD_ENGAGEMENT_TARGET_START', datetime.date.today().isoformat()) self.engagement_target_end = os.getenv('DD_ENGAGEMENT_TARGET_END', '2999-12-31') self.test_name = os.getenv('DD_TEST_NAME') @@ -60,6 +62,8 @@ def check_environment_reimport_findings(self): print('DD_PRODUCT_TYPE_NAME: ', self.product_type_name) print('DD_PRODUCT_NAME: ', self.product_name) print('DD_ENGAGEMENT_NAME: ', self.engagement_name) + print('DD_ENGAGEMENT_VERSION: ', self.engagement_version) + print('DD_ENGAGEMENT_DEDUPLICATION: ', self.engagement_deduplication) print('DD_ENGAGEMENT_TARGET_START: ', self.engagement_target_start) print('DD_ENGAGEMENT_TARGET_END: ', self.engagement_target_end) print('DD_TEST_NAME: ', self.test_name) diff --git a/unittests/test_api.py b/unittests/test_api.py index 4b10443..653dea0 100644 --- a/unittests/test_api.py +++ b/unittests/test_api.py @@ -156,7 +156,7 @@ def test_new_product(self, mockPost, mockEnv): def test_get_engagement_found(self, mockGet, mockEnv): response = Mock(spec=Response) response.status_code = 200 - response.text = '{\"count\": 2, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\"}, {\"id\": 3, \"name\": \"engagement\"}]}' + response.text = '{\"count\": 2, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\", \"version\": \"null\"}, {\"id\": 3, \"name\": \"engagement\", \"version\": \"null\"}, {\"id\": 4, \"name\": \"engagement\", \"version\": \"1.0.1\"}]}' mockGet.return_value = response api = Api() @@ -168,6 +168,27 @@ def test_get_engagement_found(self, mockGet, mockEnv): mockGet.assert_called_once_with(url, headers=self.header, params=payload, verify=True) response.raise_for_status.assert_called_once() + @patch('dd_import.environment.Environment') + @patch('requests.get') + @patch.dict('os.environ', {'DD_URL': 'https://example.com', + 'DD_API_KEY': 'api_key', + 'DD_ENGAGEMENT_NAME': 'engagement', + 'DD_ENGAGEMENT_VERSION': '1.0.1'}) + def test_get_engagement_with_version_found(self, mockGet, mockEnv): + response = Mock(spec=Response) + response.status_code = 200 + response.text = '{\"count\": 2, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\", \"version\": \"null\"}, {\"id\": 3, \"name\": \"engagement\", \"version\": \"null\"}, {\"id\": 4, \"name\": \"engagement\", \"version\": \"1.0.1\"}]}' + mockGet.return_value = response + + api = Api() + id = api.get_engagement(self.product_id) + + self.assertEqual(id, self.engagement_id) + url = 'https://example.com/api/v2/engagements/' + payload = {'name': 'engagement', 'product': self.product_id, 'version': '1.0.1'} + mockGet.assert_called_once_with(url, headers=self.header, params=payload, verify=True) + response.raise_for_status.assert_called_once() + @patch('dd_import.environment.Environment') @patch('requests.get') @patch('dd_import.dd_api.Api.new_engagement') @@ -191,6 +212,50 @@ def test_get_engagement_not_found(self, mockNewEngagement, mockGet, mockEnv): mockNewEngagement.assert_called_once_with(self.product_id) response.raise_for_status.assert_called_once() + @patch('dd_import.environment.Environment') + @patch('requests.post') + @patch.dict('os.environ', {'DD_URL': 'https://example.com', + 'DD_API_KEY': 'api_key', + 'DD_ENGAGEMENT_NAME': 'engagement', + 'DD_ENGAGEMENT_VERSION': '1.0.1'}) + def test_new_engagement_with_version(self, mockPost, mockEnv): + response = Mock(spec=Response) + response.status_code = 200 + response.text = '{\"id\": 3}' + mockPost.return_value = response + + api = Api() + id = api.new_engagement(self.product_id) + + self.assertEqual(id, self.engagement_id) + today = datetime.date.today().isoformat() + url = 'https://example.com/api/v2/engagements/' + payload = f'{{"name": "engagement", "product": 2, "target_start": "{today}", "target_end": "2999-12-31", "engagement_type": "CI/CD", "deduplication_on_engagement": false, "status": "In Progress", "version": "1.0.1"}}' + mockPost.assert_called_once_with(url, headers=self.header, data=payload, verify=True) + response.raise_for_status.assert_called_once() + + @patch('dd_import.environment.Environment') + @patch('requests.post') + @patch.dict('os.environ', {'DD_URL': 'https://example.com', + 'DD_API_KEY': 'api_key', + 'DD_ENGAGEMENT_NAME': 'engagement', + 'DD_ENGAGEMENT_DEDUPLICATION': 'True'}) + def test_new_engagement_with_deduplication(self, mockPost, mockEnv): + response = Mock(spec=Response) + response.status_code = 200 + response.text = '{\"id\": 3}' + mockPost.return_value = response + + api = Api() + id = api.new_engagement(self.product_id) + + self.assertEqual(id, self.engagement_id) + today = datetime.date.today().isoformat() + url = 'https://example.com/api/v2/engagements/' + payload = f'{{"name": "engagement", "product": 2, "target_start": "{today}", "target_end": "2999-12-31", "engagement_type": "CI/CD", "deduplication_on_engagement": true, "status": "In Progress"}}' + mockPost.assert_called_once_with(url, headers=self.header, data=payload, verify=True) + response.raise_for_status.assert_called_once() + @patch('dd_import.environment.Environment') @patch('requests.post') @patch.dict('os.environ', {'DD_URL': 'https://example.com', @@ -208,7 +273,7 @@ def test_new_engagement_without_target(self, mockPost, mockEnv): self.assertEqual(id, self.engagement_id) today = datetime.date.today().isoformat() url = 'https://example.com/api/v2/engagements/' - payload = f'{{"name": "engagement", "product": 2, "target_start": "{today}", "target_end": "2999-12-31", "engagement_type": "CI/CD", "status": "In Progress"}}' + payload = f'{{"name": "engagement", "product": 2, "target_start": "{today}", "target_end": "2999-12-31", "engagement_type": "CI/CD", "deduplication_on_engagement": false, "status": "In Progress"}}' mockPost.assert_called_once_with(url, headers=self.header, data=payload, verify=True) response.raise_for_status.assert_called_once() @@ -230,7 +295,7 @@ def test_new_engagement_with_target(self, mockPost, mockEnv): self.assertEqual(id, self.engagement_id) url = 'https://example.com/api/v2/engagements/' - payload = '{"name": "engagement", "product": 2, "target_start": "2023-02-01", "target_end": "2023-02-28", "engagement_type": "CI/CD", "status": "In Progress"}' + payload = '{"name": "engagement", "product": 2, "target_start": "2023-02-01", "target_end": "2023-02-28", "engagement_type": "CI/CD", "deduplication_on_engagement": false, "status": "In Progress"}' mockPost.assert_called_once_with(url, headers=self.header, data=payload, verify=True) response.raise_for_status.assert_called_once() @@ -252,7 +317,7 @@ def test_new_engagement_with_source_code_management_uri(self, mockPost, mockEnv) self.assertEqual(id, self.engagement_id) today = datetime.date.today().isoformat() url = 'https://example.com/api/v2/engagements/' - payload = f'{{"name": "engagement", "product": 2, "target_start": "{today}", "target_end": "2999-12-31", "engagement_type": "CI/CD", "status": "In Progress", "source_code_management_uri": "https://github.com/MyOrg/MyProject/tree/main"}}' + payload = f'{{"name": "engagement", "product": 2, "target_start": "{today}", "target_end": "2999-12-31", "engagement_type": "CI/CD", "deduplication_on_engagement": false, "status": "In Progress", "source_code_management_uri": "https://github.com/MyOrg/MyProject/tree/main"}}' mockPost.assert_called_once_with(url, headers=self.header, data=payload, verify=True) response.raise_for_status.assert_called_once() From 8231cf49e5382748e23d166e8a66c63fdc117848 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 28 Aug 2024 15:08:03 +0000 Subject: [PATCH 2/2] update result count --- unittests/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/test_api.py b/unittests/test_api.py index 653dea0..9e23968 100644 --- a/unittests/test_api.py +++ b/unittests/test_api.py @@ -156,7 +156,7 @@ def test_new_product(self, mockPost, mockEnv): def test_get_engagement_found(self, mockGet, mockEnv): response = Mock(spec=Response) response.status_code = 200 - response.text = '{\"count\": 2, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\", \"version\": \"null\"}, {\"id\": 3, \"name\": \"engagement\", \"version\": \"null\"}, {\"id\": 4, \"name\": \"engagement\", \"version\": \"1.0.1\"}]}' + response.text = '{\"count\": 3, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\", \"version\": \"null\"}, {\"id\": 3, \"name\": \"engagement\", \"version\": \"null\"}, {\"id\": 4, \"name\": \"engagement\", \"version\": \"1.0.1\"}]}' mockGet.return_value = response api = Api() @@ -177,7 +177,7 @@ def test_get_engagement_found(self, mockGet, mockEnv): def test_get_engagement_with_version_found(self, mockGet, mockEnv): response = Mock(spec=Response) response.status_code = 200 - response.text = '{\"count\": 2, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\", \"version\": \"null\"}, {\"id\": 3, \"name\": \"engagement\", \"version\": \"null\"}, {\"id\": 4, \"name\": \"engagement\", \"version\": \"1.0.1\"}]}' + response.text = '{\"count\": 3, \"results\": [{\"id\": 2, \"name\": \"engagement_dev\", \"version\": \"null\"}, {\"id\": 3, \"name\": \"engagement\", \"version\": \"null\"}, {\"id\": 4, \"name\": \"engagement\", \"version\": \"1.0.1\"}]}' mockGet.return_value = response api = Api()