Skip to content

Commit d345c71

Browse files
committed
VED-79:Setting up MNS post notifications to SQS
1 parent ce99437 commit d345c71

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

azure/azure-pr-pipeline.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ extends:
4040
- template: ./templates/post-deploy.yml
4141
parameters:
4242
aws_account_type: 'dev'
43+
- script: |
44+
echo "Creating MNS subscription..."
45+
python3 backend/src/mns/subscribe.py
46+
displayName: "Subscribe SQS to MNS signal"
4347
- environment: internal-dev-sandbox
4448
proxy_path: sandbox
4549
jinja_templates:

backend/src/mns_service.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import requests
2+
import uuid
3+
import json
4+
from authentication import AppRestrictedAuth
5+
from models.errors import UnhandledResponseError
6+
7+
SQS_ARN = "arn:aws:sqs:eu-west-2:345594581768:imms-pr-655-id-sync-queue"
8+
MNS_URL = "https://int.api.service.nhs.uk/multicast-notification-service/subscriptions"
9+
10+
class mnsService:
11+
def __init__(self, authenticator: AppRestrictedAuth):
12+
self.authenticator = authenticator
13+
14+
15+
16+
def subscribeNotification(self) -> dict | None:
17+
access_token = self.authenticator.get_access_token()
18+
request_headers = {
19+
'Authorization': f'Bearer {access_token}',
20+
'X-Correlation-ID': str(uuid.uuid4())
21+
}
22+
23+
subscription_payload = {
24+
"resourceType": "Subscription",
25+
"status": "requested",
26+
"reason": "Subscribe SQS to MNS test-signal",
27+
"criteria": "eventType=mns-test-signal-1",
28+
"channel": {
29+
"type": "message",
30+
"endpoint": SQS_ARN,
31+
"payload": "application/json"
32+
}
33+
}
34+
response = requests.post(MNS_URL, headers=request_headers, data=json.dumps(subscription_payload))
35+
36+
if response.status_code == 200:
37+
return response.json()
38+
elif response.status_code == 404:
39+
return None
40+
else:
41+
msg = "Please provide the correct resource type for this endpoint"
42+
raise UnhandledResponseError(response=response.json(), message=msg)
43+
44+
45+
# Unsubscribe to the PR - destroying the notification
46+
def unsubscribeNotication():
47+
pass

backend/src/subscribe_mns.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
3+
from authentication import AppRestrictedAuth
4+
from mns_service import mnsService
5+
6+
if __name__ == "__main__":
7+
auth = AppRestrictedAuth() # Use appropriate params if needed
8+
mns = mnsService(authenticator=auth)
9+
result = mns.subscribeNotification()
10+
print("Subscription Result:", result)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock, Mock, create_autospec
3+
from mns_service import mnsService # adjust import if needed
4+
from authentication import AppRestrictedAuth
5+
from models.errors import UnhandledResponseError
6+
7+
8+
class TestMnsService(unittest.TestCase):
9+
10+
def setUp(self):
11+
# Common mock setup
12+
self.authenticator = create_autospec(AppRestrictedAuth)
13+
self.authenticator.get_access_token.return_value = "mocked_token"
14+
self.mock_secret_manager = Mock()
15+
self.mock_cache = Mock()
16+
17+
@patch("mns_service.requests.post")
18+
def test_successful_subscription(self, mock_post):
19+
# Arrange
20+
mock_response = MagicMock()
21+
mock_response.status_code = 200
22+
mock_response.json.return_value = {"subscriptionId": "abc123"}
23+
mock_post.return_value = mock_response
24+
25+
service = mnsService(self.authenticator)
26+
27+
# Act
28+
result = service.subscribeNotification()
29+
30+
# Assert
31+
self.assertEqual(result, {"subscriptionId": "abc123"})
32+
mock_post.assert_called_once()
33+
self.authenticator.get_access_token.assert_called_once()
34+
35+
@patch("mns_service.requests.post")
36+
def test_not_found_subscription(self, mock_post):
37+
mock_response = MagicMock()
38+
mock_response.status_code = 404
39+
mock_post.return_value = mock_response
40+
41+
service = mnsService(self.authenticator)
42+
result = service.subscribeNotification()
43+
44+
self.assertIsNone(result)
45+
46+
@patch("mns_service.requests.post")
47+
def test_unhandled_error(self, mock_post):
48+
mock_response = MagicMock()
49+
mock_response.status_code = 500
50+
mock_response.json.return_value = {"error": "Server error"}
51+
mock_post.return_value = mock_response
52+
53+
service = mnsService(self.authenticator)
54+
55+
with self.assertRaises(UnhandledResponseError) as context:
56+
service.subscribeNotification()
57+
58+
self.assertIn("Please provide the correct resource type", str(context.exception))
59+
60+
if __name__ == "__main__":
61+
unittest.main()

0 commit comments

Comments
 (0)