Skip to content

Commit 98a37c0

Browse files
committed
VED-79:setup script in the pipeline
1 parent ffe77be commit 98a37c0

File tree

10 files changed

+72
-43
lines changed

10 files changed

+72
-43
lines changed

azure/templates/post-deploy.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,26 @@ steps:
6868
DYNAMODB_TABLE_NAME=$(make -s output name=dynamodb_table_name)
6969
AWS_SQS_QUEUE_NAME=$(make -s output name=aws_sqs_queue_name)
7070
AWS_SNS_TOPIC_NAME=$(make -s output name=aws_sns_topic_name)
71+
SQS_QUEUE_ARN=$(make -s output name=id_sync_queue_arn)
7172
echo "##vso[task.setvariable variable=DYNAMODB_TABLE_NAME]$DYNAMODB_TABLE_NAME"
7273
echo "##vso[task.setvariable variable=AWS_DOMAIN_NAME]$AWS_DOMAIN_NAME"
7374
echo "##vso[task.setvariable variable=IMMS_DELTA_TABLE_NAME]$IMMS_DELTA_TABLE_NAME"
7475
echo "##vso[task.setvariable variable=AWS_SQS_QUEUE_NAME]$AWS_SQS_QUEUE_NAME"
7576
echo "##vso[task.setvariable variable=AWS_SNS_TOPIC_NAME]$AWS_SNS_TOPIC_NAME"
77+
echo "##vso[task.setvariable variable=SQS_QUEUE_ARN]$SQS_QUEUE_ARN"
7678
fi
7779
displayName: Apply Terraform
7880
workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)"
7981
retryCountOnTaskFailure: 2
8082
83+
- bash: |
84+
echo "Subscribing SQS to MNS via Lambda-compatible client..."
85+
poetry run python mns_subscription/src/subscribe_mns.py
86+
displayName: "Run MNS Subscription"
87+
workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)"
88+
env:
89+
SQS_ARN: "$(SQS_ARN)"
90+
8191
- bash: |
8292
set -ex
8393

mns_subscription/.coverage

52 KB
Binary file not shown.

mns_subscription/models/errors.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import uuid
22
from dataclasses import dataclass
33
from enum import Enum
4-
from typing import Union
54

65

76
class Severity(str, Enum):
@@ -70,7 +69,7 @@ def to_operation_outcome(self) -> dict:
7069
class UnhandledResponseError(RuntimeError):
7170
"""Use this error when the response from an external service (ex: dynamodb) can't be handled"""
7271

73-
response: Union[dict, str]
72+
response: dict | str
7473
message: str
7574

7675
def __str__(self):

mns_subscription/src/cache.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,24 @@ def __init__(self, directory):
1111
self.cache_file.seek(0)
1212
content = self.cache_file.read()
1313
if len(content) == 0:
14-
self.cache = {}
14+
self.store = {}
1515
else:
16-
self.cache = json.loads(content)
16+
self.store = json.loads(content)
1717

1818
def put(self, key: str, value: dict):
19-
self.cache[key] = value
19+
self.store[key] = value
2020
self._overwrite()
2121

2222
def get(self, key: str) -> Optional[dict]:
23-
return self.cache.get(key, None)
23+
return self.store.get(key, None)
2424

2525
def delete(self, key: str):
26-
if key not in self.cache:
26+
if key not in self.store:
2727
return
28-
del self.cache[key]
28+
del self.store[key]
2929

3030
def _overwrite(self):
3131
with open(self.cache_file.name, "w") as self.cache_file:
3232
self.cache_file.seek(0)
33-
self.cache_file.write(json.dumps(self.cache))
33+
self.cache_file.write(json.dumps(self.store))
3434
self.cache_file.truncate()

mns_subscription/src/mns_service.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import requests
2+
import os
23
import uuid
34
import json
45
from authentication import AppRestrictedAuth
56
from models.errors import UnhandledResponseError
67

7-
SQS_ARN = "arn:aws:sqs:eu-west-2:345594581768:imms-pr-655-id-sync-queue"
8+
SQS_ARN = os.getenv("SQS_QUEUE_ARN")
89
MNS_URL = "https://int.api.service.nhs.uk/multicast-notification-service/subscriptions"
910

1011

1112
class MnsService:
1213
def __init__(self, authenticator: AppRestrictedAuth):
1314
self.authenticator = authenticator
1415

15-
def subscribeNotification(self) -> dict | None:
16+
def subscribe_notification(self) -> dict | None:
1617
access_token = self.authenticator.get_access_token()
1718
request_headers = {
1819
'Authorization': f'Bearer {access_token}',

mns_subscription/src/subscribe_mns.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
from authentication import AppRestrictedAuth
22
import os
3+
import logging
34
from cache import Cache
45
from mns_service import MnsService
56
import boto3
67
from authentication import Service
78
from botocore.config import Config
89

10+
logging.basicConfig(level=logging.INFO)
11+
912

1013
def run_subscription():
11-
mns_env: str = os.getenv("MNS_ENV", "int")
12-
13-
boto_config = Config(region_name="eu-west-2")
14-
cache = Cache(directory="/tmp")
15-
authenticator = AppRestrictedAuth(
16-
service=Service.MNS,
17-
secret_manager_client=boto3.client("secretsmanager", config=boto_config),
18-
environment=mns_env,
19-
cache=cache,
20-
)
21-
mns = MnsService(authenticator, mns_env)
22-
return mns.subscribeNotification()
14+
try:
15+
mns_env: str = os.getenv("MNS_ENV", "int")
16+
17+
boto_config = Config(region_name="eu-west-2")
18+
cache = Cache(directory="/tmp")
19+
logging.info("Creating authenticator...")
20+
authenticator = AppRestrictedAuth(
21+
service=Service.MNS,
22+
secret_manager_client=boto3.client("secretsmanager", config=boto_config),
23+
environment=mns_env,
24+
cache=cache,
25+
)
26+
27+
logging.info("Creating MNS service...")
28+
mns = MnsService(authenticator, mns_env)
29+
30+
logging.info("Subscribing to MNS...")
31+
result = mns.subscribe_notification()
32+
logging.info(f"Subscription Result: {result}")
33+
return result
34+
except Exception:
35+
logging.exception("Failed to complete MNS subscription process")
36+
raise
2337

2438

2539
if __name__ == "__main__":

mns_subscription/tests/test_cache.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@
77

88
class TestCache(unittest.TestCase):
99
def setUp(self):
10-
self.cache = Cache(tempfile.gettempdir())
10+
self.store = Cache(tempfile.gettempdir())
1111

1212
def test_cache_put(self):
1313
"""it should store cache in specified key"""
1414
value = {"foo": "a-foo", "bar": 42}
1515
key = "a_key"
1616

1717
# When
18-
self.cache.put(key, value)
19-
act_value = self.cache.get(key)
18+
self.store.put(key, value)
19+
act_value = self.store.get(key)
2020

2121
# Then
2222
self.assertDictEqual(value, act_value)
@@ -25,45 +25,45 @@ def test_cache_put_overwrite(self):
2525
"""it should store updated cache value"""
2626
value = {"foo": "a-foo", "bar": 42}
2727
key = "a_key"
28-
self.cache.put(key, value)
28+
self.store.put(key, value)
2929

3030
new_value = {"foo": "new-foo"}
31-
self.cache.put(key, new_value)
31+
self.store.put(key, new_value)
3232

3333
# When
34-
updated_value = self.cache.get(key)
34+
updated_value = self.store.get(key)
3535

3636
# Then
3737
self.assertDictEqual(new_value, updated_value)
3838

3939
def test_key_not_found(self):
4040
"""it should return None if key doesn't exist"""
41-
value = self.cache.get("it-does-not-exist")
41+
value = self.store.get("it-does-not-exist")
4242
self.assertIsNone(value)
4343

4444
def test_delete(self):
4545
"""it should delete key"""
4646
key = "a_key"
47-
self.cache.put(key, {"a": "b"})
48-
self.cache.delete(key)
47+
self.store.put(key, {"a": "b"})
48+
self.store.delete(key)
4949

50-
value = self.cache.get(key)
50+
value = self.store.get(key)
5151
self.assertIsNone(value)
5252

5353
def test_write_to_file(self):
5454
"""it should update the cache file"""
5555
value = {"foo": "a-long-foo-so-to-make-sure-truncate-is-working", "bar": 42}
5656
key = "a_key"
57-
self.cache.put(key, value)
57+
self.store.put(key, value)
5858
# Add one and delete to make sure file gets updated
59-
self.cache.put("to-delete-key", {"x": "y"})
60-
self.cache.delete("to-delete-key")
59+
self.store.put("to-delete-key", {"x": "y"})
60+
self.store.delete("to-delete-key")
6161

6262
# When
6363
new_value = {"a": "b"}
64-
self.cache.put(key, new_value)
64+
self.store.put(key, new_value)
6565

6666
# Then
67-
with open(self.cache.cache_file.name, "r") as stored:
67+
with open(self.store.cache_file.name, "r") as stored:
6868
content = json.loads(stored.read())
6969
self.assertDictEqual(content[key], new_value)

mns_subscription/tests/test_mns_service.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_successful_subscription(self, mock_post):
2424
service = MnsService(self.authenticator)
2525

2626
# Act
27-
result = service.subscribeNotification()
27+
result = service.subscribe_notification()
2828

2929
# Assert
3030
self.assertEqual(result, {"subscriptionId": "abc123"})
@@ -38,7 +38,7 @@ def test_not_found_subscription(self, mock_post):
3838
mock_post.return_value = mock_response
3939

4040
service = MnsService(self.authenticator)
41-
result = service.subscribeNotification()
41+
result = service.subscribe_notification()
4242

4343
self.assertIsNone(result)
4444

@@ -52,7 +52,7 @@ def test_unhandled_error(self, mock_post):
5252
service = MnsService(self.authenticator)
5353

5454
with self.assertRaises(UnhandledResponseError) as context:
55-
service.subscribeNotification()
55+
service.subscribe_notification()
5656

5757
self.assertIn("Please provide the correct resource type", str(context.exception))
5858

mns_subscription/tests/test_subscription.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def test_run_subscription_success(self, mock_boto_client, mock_auth_class, mock_
1717
mock_auth_class.return_value = mock_auth_instance
1818

1919
mock_mns_instance = MagicMock()
20-
mock_mns_instance.subscribeNotification.return_value = {"subscriptionId": "abc123"}
20+
mock_mns_instance.subscribe_notification.return_value = {"subscriptionId": "abc123"}
2121
mock_mns_service.return_value = mock_mns_instance
2222

2323
# Act
@@ -27,7 +27,7 @@ def test_run_subscription_success(self, mock_boto_client, mock_auth_class, mock_
2727
self.assertEqual(result, {"subscriptionId": "abc123"})
2828
mock_auth_class.assert_called_once()
2929
mock_mns_service.assert_called_once_with(mock_auth_instance, "int")
30-
mock_mns_instance.subscribeNotification.assert_called_once()
30+
mock_mns_instance.subscribe_notification.assert_called_once()
3131

3232

3333
if __name__ == "__main__":

terraform/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ output "aws_sqs_queue_name" {
1717
output "aws_sns_topic_name" {
1818
value = aws_sns_topic.delta_sns.name
1919
}
20+
21+
output "id_sync_queue_arn" {
22+
description = "The ARN of the created SQS queue"
23+
value = aws_sqs_queue.id_sync_queue.arn
24+
}

0 commit comments

Comments
 (0)