Skip to content

Commit d49c4ad

Browse files
committed
VED-79: sandbox pipeline
1 parent 3a7246b commit d49c4ad

File tree

7 files changed

+172
-17
lines changed

7 files changed

+172
-17
lines changed

azure/azure-pr-pipeline.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ extends:
4848
- template: ./templates/post-deploy.yml
4949
parameters:
5050
aws_account_type: 'dev'
51+
subscribe_to_mns: 'true'

azure/templates/post-deploy.yml

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,34 @@ steps:
7777
displayName: Apply Terraform
7878
workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)"
7979
retryCountOnTaskFailure: 2
80+
parameters:
81+
- name: aws_account_type
82+
type: string
83+
default: 'dev'
84+
- name: subscribe_mns
85+
type: boolean
86+
default: true # Set to true by default, false where you want to skip
8087

88+
steps:
89+
- ${{ if eq(parameters.subscribe_mns, true) }}:
8190
- bash: |
82-
export AWS_PROFILE=apim-dev
83-
echo "Subscribing SQS to MNS for notifications."
84-
pyenv install -s 3.11.11
85-
pyenv local 3.11.11
86-
echo "Setting up poetry environment..."
87-
poetry env use 3.11
88-
poetry install --no-root
89-
90-
echo "Setting PYTHONPATH..."
91-
export PYTHONPATH=$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/mns_subscription
92-
93-
echo "Subscribing SQS to MNS for notifications..."
94-
poetry run python src/subscribe_mns.py
95-
displayName: "Run MNS Subscription"
96-
workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/mns_subscription"
97-
env:
98-
SQS_ARN: "$(ID_SYNC_QUEUE_ARN)"
91+
export AWS_PROFILE=apim-dev
92+
echo "Subscribing SQS to MNS for notifications."
93+
pyenv install -s 3.11.11
94+
pyenv local 3.11.11
95+
echo "Setting up poetry environment..."
96+
poetry env use 3.11
97+
poetry install --no-root
98+
99+
echo "Setting PYTHONPATH..."
100+
export PYTHONPATH=$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/mns_subscription
101+
102+
echo "Subscribing SQS to MNS for notifications..."
103+
poetry run python src/subscribe_mns.py
104+
displayName: "Run MNS Subscription"
105+
workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/mns_subscription"
106+
env:
107+
SQS_ARN: "$(ID_SYNC_QUEUE_ARN)"
99108
100109
- bash: |
101110
set -ex

mns_subscription/src/mns_service.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,37 @@ def check_subscription(self) -> dict:
118118
except Exception as e:
119119
logging.error(f"Error ensuring subscription: {e}")
120120
raise
121+
122+
def delete_subscription(self, subscription_id: str) -> bool:
123+
"""Delete the subscription by ID."""
124+
url = f"{MNS_URL}/{subscription_id}"
125+
response = requests.delete(url, headers=self.request_headers)
126+
if response.status_code in (200, 204):
127+
logging.info(f"Deleted subscription {subscription_id}")
128+
return True
129+
elif response.status_code == 401:
130+
raise TokenValidationError(response=response.json(), message="Token validation failed for the request")
131+
elif response.status_code == 404:
132+
raise ResourceFoundError(response=response.json(), message=f"Subscription {subscription_id} not found")
133+
elif response.status_code == 403:
134+
raise UnauthorizedError(response=response.json(), message="No permission to delete subscription")
135+
elif response.status_code == 500:
136+
raise ServerError(response=response.json(), message="Internal Server Error")
137+
else:
138+
raise UnhandledResponseError(response=response.json(), message=f"Unhandled error: {response.status_code}")
139+
140+
def check_delete_subcription(self):
141+
try:
142+
resource = self.get_subscription() # Get the full resource dict
143+
if not resource:
144+
return "No matching subscription found to delete."
145+
146+
subscription_id = resource.get("id")
147+
if not subscription_id:
148+
return "Subscription resource missing 'id' field."
149+
150+
self.delete_subscription(subscription_id)
151+
return "Subscription successfully deleted"
152+
except Exception as e:
153+
# Optionally log the exception here
154+
return f"Error deleting subscription: {str(e)}"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import logging
2+
from cache import Cache
3+
from mns_service import MnsService
4+
import boto3
5+
from authentication import AppRestrictedAuth, Service
6+
from botocore.config import Config
7+
8+
logging.basicConfig(level=logging.INFO)
9+
10+
11+
def run_subscription():
12+
try:
13+
mns_env: str = "int"
14+
15+
boto_config = Config(region_name="eu-west-2")
16+
cache = Cache(directory="/tmp")
17+
logging.info("Creating authenticator...")
18+
authenticator = AppRestrictedAuth(
19+
service=Service.MNS,
20+
secret_manager_client=boto3.client("secretsmanager", config=boto_config),
21+
environment=mns_env,
22+
cache=cache,
23+
)
24+
25+
logging.info("Creating MNS service...")
26+
mns = MnsService(authenticator)
27+
28+
logging.info("Deleting MNS Subscription...")
29+
result = mns.check_delete_subscription()
30+
logging.info(f"Subscription Result: {result}")
31+
return result
32+
except Exception:
33+
logging.exception("Failed to complete MNS subscription process")
34+
raise
35+
36+
37+
if __name__ == "__main__":
38+
result = run_subscription()
39+
print("Subscription Result:", result)

mns_subscription/tests/test_mns_service.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,45 @@ def test_check_subscription_creates_if_not_found(self, mock_get, mock_post):
143143
mock_get.assert_called_once()
144144
mock_post.assert_called_once()
145145

146+
@patch.object(MnsService, "delete_subscription")
147+
@patch.object(MnsService, "get_subscription")
148+
def test_check_delete_subscription_success(self, mock_get_subscription, mock_delete_subscription):
149+
# Mock get_subscription returns a resource with id
150+
mock_get_subscription.return_value = {"id": "sub-123"}
151+
# Mock delete_subscription returns True
152+
mock_delete_subscription.return_value = True
153+
154+
service = MnsService(self.authenticator)
155+
result = service.check_delete_subcription()
156+
self.assertEqual(result, "Subscription successfully deleted")
157+
mock_get_subscription.assert_called_once()
158+
mock_delete_subscription.assert_called_once_with("sub-123")
159+
160+
@patch.object(MnsService, "get_subscription")
161+
def test_check_delete_subscription_no_resource(self, mock_get_subscription):
162+
# No subscription found
163+
mock_get_subscription.return_value = None
164+
service = MnsService(self.authenticator)
165+
result = service.check_delete_subcription()
166+
self.assertEqual(result, "No matching subscription found to delete.")
167+
168+
@patch.object(MnsService, "get_subscription")
169+
def test_check_delete_subscription_missing_id(self, mock_get_subscription):
170+
# Resource with no id field
171+
mock_get_subscription.return_value = {"not_id": "nope"}
172+
service = MnsService(self.authenticator)
173+
result = service.check_delete_subcription()
174+
self.assertEqual(result, "Subscription resource missing 'id' field.")
175+
176+
@patch.object(MnsService, "delete_subscription")
177+
@patch.object(MnsService, "get_subscription")
178+
def test_check_delete_subscription_raises(self, mock_get_subscription, mock_delete_subscription):
179+
mock_get_subscription.return_value = {"id": "sub-123"}
180+
mock_delete_subscription.side_effect = Exception("Error!")
181+
service = MnsService(self.authenticator)
182+
result = service.check_delete_subcription()
183+
self.assertTrue(result.startswith("Error deleting subscription: Error!"))
184+
146185

147186
if __name__ == "__main__":
148187
unittest.main()
File renamed without changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock
3+
from unsubscribe_mns import run_subscription
4+
5+
6+
class TestRunSubscription(unittest.TestCase):
7+
8+
@patch("unsubscribe_mns.MnsService")
9+
@patch("unsubscribe_mns.AppRestrictedAuth")
10+
@patch("unsubscribe_mns.boto3.client")
11+
def test_run_subscription_success(self, mock_boto_client, mock_auth_class, mock_mns_service):
12+
# Arrange
13+
mock_secrets_client = MagicMock()
14+
mock_boto_client.return_value = mock_secrets_client
15+
16+
mock_auth_instance = MagicMock()
17+
mock_auth_class.return_value = mock_auth_instance
18+
19+
mock_mns_instance = MagicMock()
20+
mock_mns_instance.check_delete_subscription.return_value = {"Subscription successfully deleted"}
21+
mock_mns_service.return_value = mock_mns_instance
22+
23+
# Act
24+
result = run_subscription()
25+
26+
# Assert
27+
self.assertEqual(result, {"Subscription successfully deleted"})
28+
mock_auth_class.assert_called_once()
29+
mock_mns_instance.check_delete_subscription.assert_called_once()
30+
31+
32+
if __name__ == "__main__":
33+
unittest.main()

0 commit comments

Comments
 (0)