Skip to content

Commit af60add

Browse files
committed
Merge branch 'master' into VED-737-Search-API--element-throws-error-message
2 parents cdc3701 + c53c49d commit af60add

32 files changed

+430
-232
lines changed
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
name: Deploy Blue Green - INT
22

33
on:
4-
pull_request:
5-
types: [closed]
6-
branches: [master]
4+
push:
5+
branches:
6+
- release-2025-08-12
77

88
jobs:
99
deploy-green:
1010
uses: ./.github/workflows/deploy-template.yml
1111
with:
12-
environment: green
13-
12+
apigee_environment: int
13+
environment: preprod
14+
sub_environment: int-green
1415
deploy-blue:
15-
needs: deploy-green
1616
uses: ./.github/workflows/deploy-template.yml
1717
with:
18-
environment: blue
18+
apigee_environment: int
19+
environment: preprod
20+
sub_environment: int-blue
Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
name: Deploy to INT and run E2e test
1+
name: Deploy Template
2+
23
on:
34
workflow_call:
45
inputs:
6+
apigee_environment:
7+
required: true
8+
type: string
59
environment:
610
required: true
711
type: string
12+
sub_environment:
13+
required: true
14+
type: string
815

916
jobs:
1017
terraform-plan:
@@ -13,8 +20,8 @@ jobs:
1320
id-token: write
1421
contents: read
1522
steps:
16-
- name: Debug OIDC
17-
uses: aws-actions/configure-aws-credentials@v4
23+
- name: Connect to AWS
24+
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a
1825
with:
1926
aws-region: eu-west-2
2027
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/auto-ops
@@ -24,26 +31,24 @@ jobs:
2431
run: aws sts get-caller-identity
2532

2633
- name: Checkout
27-
uses: actions/checkout@v5
34+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
2835
with:
2936
fetch-depth: 1
3037

31-
- uses: hashicorp/setup-terraform@v3
38+
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd
3239
with:
3340
terraform_version: "1.12.2"
3441

3542
- name: Terraform Init
3643
working-directory: ${{ vars.TERRAFORM_DIR_PATH }}
37-
run: |
38-
export ENVIRONMENT=${{ inputs.environment }}
39-
make init
44+
run: make init apigee_environment=${{ inputs.apigee_environment }} environment=${{ inputs.environment }} sub_environment=${{ inputs.sub_environment }}
4045

4146
- name: Terraform Plan
4247
working-directory: ${{ vars.TERRAFORM_DIR_PATH }}
43-
run: |
44-
make plan environment=${{ inputs.environment }} aws_account_name=int
45-
48+
run: make plan apigee_environment=${{ inputs.apigee_environment }} environment=${{ inputs.environment }} sub_environment=${{ inputs.sub_environment }}
49+
# TODO - save the plan and use it in the apply step
4650
terraform-apply:
51+
if: ${{ vars.SKIP_APPLY != 'true' }}
4752
needs: terraform-plan
4853
runs-on: ubuntu-latest
4954
permissions:
@@ -53,55 +58,61 @@ jobs:
5358
name: int
5459
steps:
5560
- name: Checkout
56-
uses: actions/checkout@v5
61+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
5762

58-
- uses: aws-actions/configure-aws-credentials@v4
63+
- uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a
5964
with:
6065
aws-region: eu-west-2
6166
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/auto-ops
6267
role-session-name: github-actions
6368

64-
- uses: hashicorp/setup-terraform@v3
69+
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd
6570
with:
6671
terraform_version: "1.12.2"
6772

6873
- name: Terraform Init
6974
working-directory: ${{ vars.TERRAFORM_DIR_PATH }}
70-
run: |
71-
export ENVIRONMENT=${{ inputs.environment }}
72-
make init
75+
run: make init apigee_environment=${{ inputs.apigee_environment }} environment=${{ inputs.environment }} sub_environment=${{ inputs.sub_environment }}
7376

7477
- name: Terraform Apply
7578
working-directory: ${{ vars.TERRAFORM_DIR_PATH }}
76-
run: |
77-
make apply environment=${{ inputs.environment }} aws_account_name=int
78-
79+
run: make apply apigee_environment=${{ inputs.apigee_environment }} environment=${{ inputs.environment }} sub_environment=${{ inputs.sub_environment }}
80+
# TODO - use a saved plan from the plan step
7981
e2e-tests:
82+
if: ${{ vars.RUN_E2E == 'true' && inputs.sub_environment == vars.ACTIVE_ENVIRONMENT }}
8083
needs: terraform-apply
81-
if: ${{ vars.RUN_E2E == 'true' || inputs.environment == vars.ACTIVE_ENVIRONMENT }}
8284
runs-on: ubuntu-latest
8385
permissions:
8486
id-token: write
8587
contents: read
8688
steps:
8789
- name: Checkout
88-
uses: actions/checkout@v5
90+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
8991

90-
- uses: aws-actions/configure-aws-credentials@v4
92+
- uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a
9193
with:
9294
aws-region: eu-west-2
9395
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/auto-ops
9496
role-session-name: github-actions
9597

98+
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd
99+
with:
100+
terraform_version: "1.12.2"
101+
102+
- name: Terraform Init
103+
working-directory: ${{ vars.TERRAFORM_DIR_PATH }}
104+
run: make init apigee_environment=${{ inputs.apigee_environment }} environment=${{ inputs.environment }} sub_environment=${{ inputs.sub_environment }}
105+
96106
- name: Set up Python
97107
uses: actions/setup-python@v5
98108
with:
99109
python-version: "3.11"
100110

101111
- name: Install Poetry
102112
run: |
103-
curl -sSL https://install.python-poetry.org | python3 -
113+
curl -sSL https://install.python-poetry.org | python3 - --version 2.1.4
104114
echo "$HOME/.local/bin" >> $GITHUB_PATH
115+
poetry --version
105116
106117
- name: Set Poetry to use Python 3.11
107118
working-directory: ${{ vars.E2E_DIR_PATH }}
@@ -113,27 +124,39 @@ jobs:
113124
run: |
114125
poetry install --no-root
115126
127+
- name: Install oathtool
128+
run: sudo apt-get update && sudo apt-get install -y oathtool
129+
130+
- name: Get JWT token for apigee
131+
env:
132+
APIGEE_USERNAME: ${{ vars.APIGEE_USERNAME }}
133+
APIGEE_PASSWORD: ${{ secrets.APIGEE_PASSWORD }}
134+
APIGEE_OAUTH_TOKEN: ${{ secrets.APIGEE_BASIC_AUTH_TOKEN }}
135+
APIGEE_OTP_SECRET: ${{ secrets.APIGEE_OTP_KEY }}
136+
run: |
137+
CODE=$(oathtool --totp -b "$APIGEE_OTP_SECRET")
138+
echo "::add-mask::$CODE"
139+
echo "Requesting access token from Apigee..."
140+
response=$(curl -s -X POST "https://login.apigee.com/oauth/token" \
141+
-H "Content-Type: application/x-www-form-urlencoded" \
142+
-H "Accept: application/json;charset=utf-8" \
143+
-H "Authorization: Basic $APIGEE_BASIC_AUTH_TOKEN" \
144+
-d "username=$APIGEE_USERNAME&password=$APIGEE_PASSWORD&mfa_token=$CODE&grant_type=password")
145+
token=$(echo "$response" | jq -e -r '.access_token')
146+
if [[ -z "$token" ]]; then
147+
echo "Failed to retrieve access token"
148+
exit 1
149+
fi
150+
echo "::add-mask::$token"
151+
echo "APIGEE_ACCESS_TOKEN=$token" >> $GITHUB_ENV
152+
116153
- name: Run e2e tests
117154
working-directory: ${{ vars.E2E_DIR_PATH }}
155+
env:
156+
APIGEE_ACCESS_TOKEN: ${{ env.APIGEE_ACCESS_TOKEN }}
157+
APIGEE_USERNAME: [email protected]
118158
run: |
119-
apigee_token=$(aws ssm get-parameter \
120-
--name "/imms/apigee/non-prod/token" \
121-
--with-decryption \
122-
--query "Parameter.Value" \
123-
--output text)
124-
125-
status_api_key=$(aws ssm get-parameter \
126-
--name "/imms/apigee/non-prod/status-api-key" \
127-
--with-decryption \
128-
--query "Parameter.Value" \
129-
--output text)
130-
131-
export APIGEE_ACCESS_TOKEN=$apigee_token
132-
133-
export APIGEE_ENVIRONMENT=int
134-
export STATUS_API_KEY=$status_api_key
135-
export PROXY_NAME=immunisation-fhir-api-internal-dev
136-
export SERVICE_BASE_PATH=immunisation-fhir-api/FHIR/R4
137-
export SSO_LOGIN_URL=https://login.apigee.com
138-
159+
export APIGEE_ENVIRONMENT=internal-dev
160+
export PROXY_NAME=immunisation-fhir-api-${{ inputs.sub_environment }}
161+
export SERVICE_BASE_PATH=immunisation-fhir-api/FHIR/R4-${{ inputs.sub_environment }}
139162
make run-immunization

.github/workflows/sonarcloud.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
continue-on-error: true
6565
run: |
6666
poetry install
67-
poetry run coverage run -m unittest discover -s "./tests" -p "*batch*.py" || echo "recordforwarder tests failed" >> ../failed_tests.txt
67+
poetry run coverage run -m unittest discover -p "*batch*.py" || echo "recordforwarder tests failed" >> ../failed_tests.txt
6868
poetry run coverage xml -o ../recordforwarder-coverage.xml
6969
7070
- name: Run unittest with coverage-ack-lambda

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
poetry 2.1.2
1+
poetry 2.1.4
22
nodejs 23.11.0
33
python 3.11.12

azure/azure-pr-teardown-pipeline.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ jobs:
4747
export AWS_PROFILE=apim-dev
4848
4949
cd terraform
50-
make init environment="dev" sub_environment="$WORKSPACE" bucket_name="immunisation-internal-dev-terraform-state-files"
51-
make workspace sub_environment="$WORKSPACE"
50+
make init apigee_environment=internal-dev environment=dev sub_environment="$WORKSPACE"
51+
make workspace apigee_environment=internal-dev environment=dev sub_environment="$WORKSPACE"
5252
5353
# Extract values from Terraform state before destroying
5454
ID_SYNC_QUEUE_ARN=$(make -s output name=id_sync_queue_arn)
@@ -76,6 +76,6 @@ jobs:
7676
export AWS_PROFILE=apim-dev
7777
7878
cd terraform
79-
make destroy environment="dev" sub_environment="$WORKSPACE" bucket_name="immunisation-internal-dev-terraform-state-files"
79+
make destroy apigee_environment=internal-dev environment=dev sub_environment="$WORKSPACE"
8080
displayName: Destroy terraform PR workspace and linked resources
81-
retryCountOnTaskFailure: 2
81+
retryCountOnTaskFailure: 2

azure/templates/post-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ steps:
6060
echo pr_no: $pr_no
6161
6262
cd terraform
63-
make init
63+
make init environment=${{ parameters.aws_account_type }} sub_environment=$workspace
6464
make apply environment=${{ parameters.aws_account_type }} sub_environment=$workspace
6565
6666
AWS_DOMAIN_NAME=$(make -s output name=service_domain_name)

azure/templates/post-prod-deploy.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ steps:
2626
set -e
2727
if ! [[ $APIGEE_ENVIRONMENT =~ .*-*sandbox ]]; then
2828
export AWS_PROFILE=apim-dev
29-
aws_account_no="$(aws sts get-caller-identity --query Account --output text)"
3029
3130
service_name=$(FULLY_QUALIFIED_SERVICE_NAME)
3231
@@ -35,12 +34,12 @@ steps:
3534
echo sandbox with following parameters:
3635
echo workspace: $workspace
3736
echo AWS environment: $APIGEE_ENVIRONMENT
38-
37+
3938
cd terraform
4039
41-
make init
42-
make apply aws_account_no=${aws_account_no} environment=$workspace
40+
make init environment=${{ parameters.aws_account_type }} sub_environment=$workspace
41+
make apply environment=${{ parameters.aws_account_type }} sub_environment=$workspace
4342
fi
4443
displayName: Apply Terraform
4544
workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)"
46-
retryCountOnTaskFailure: 2
45+
retryCountOnTaskFailure: 2

backend/src/batch/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import copy
2+
3+
from models.errors import MessageNotSuccessfulError
4+
5+
6+
class BatchFilenameToEventsMapper:
7+
FILENAME_NOT_PRESENT_ERROR_MSG = "Filename data was not present"
8+
9+
def __init__(self):
10+
self._filename_to_events_map: dict[str, list[dict]] = {}
11+
12+
def add_event(self, event: dict) -> None:
13+
filename_key = self._make_key(event)
14+
15+
if filename_key not in self._filename_to_events_map:
16+
self._filename_to_events_map[filename_key] = [event]
17+
return
18+
19+
self._filename_to_events_map[filename_key].append(event)
20+
21+
def get_map(self) -> dict[str, list[dict]]:
22+
return copy.deepcopy(self._filename_to_events_map)
23+
24+
def _make_key(self, event: dict) -> str:
25+
file_key = event.get("file_key")
26+
created_at_string = event.get("created_at_formatted_string")
27+
28+
if not file_key or not created_at_string:
29+
raise MessageNotSuccessfulError(self.FILENAME_NOT_PRESENT_ERROR_MSG)
30+
31+
return f"{file_key}_{created_at_string}"

backend/src/forwarding_batch_lambda.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import base64
66
import time
77
import logging
8+
9+
from batch.batch_filename_to_events_mapper import BatchFilenameToEventsMapper
810
from fhir_batch_repository import create_table
911
from fhir_batch_controller import ImmunizationBatchController, make_batch_controller
1012
from clients import sqs_client
@@ -59,7 +61,7 @@ def forward_lambda_handler(event, _):
5961
"""Forward each row to the Imms API"""
6062
logger.info("Processing started")
6163
table = create_table()
62-
array_of_messages = []
64+
filename_to_events_mapper = BatchFilenameToEventsMapper()
6365
array_of_identifiers = []
6466
controller = make_batch_controller()
6567

@@ -101,23 +103,20 @@ def forward_lambda_handler(event, _):
101103
array_of_identifiers.append(identifier)
102104

103105
imms_id = forward_request_to_dynamo(incoming_message_body, table, identifier_already_present, controller)
104-
array_of_messages.append({**base_outgoing_message_body, "imms_id": imms_id})
106+
filename_to_events_mapper.add_event({**base_outgoing_message_body, "imms_id": imms_id})
105107

106108
except Exception as error: # pylint: disable = broad-exception-caught
107-
array_of_messages.append(
109+
filename_to_events_mapper.add_event(
108110
{**base_outgoing_message_body, "diagnostics": create_diagnostics_dictionary(error)}
109111
)
110112
logger.error("Error processing message: %s", error)
111113

112114
# Send to SQS
113-
sqs_message_body = json.dumps(array_of_messages)
114-
message_len = len(sqs_message_body)
115-
logger.info(f"total message length:{message_len}")
116-
message_group_id = f"{file_key}_{created_at_formatted_string}"
117-
if message_len < 256 * 1024:
118-
sqs_client.send_message(QueueUrl=QUEUE_URL, MessageBody=sqs_message_body, MessageGroupId=message_group_id)
119-
else:
120-
logger.info("Message size exceeds 256 KB limit.Sending to sqs failed")
115+
for filename_key, events in filename_to_events_mapper.get_map().items():
116+
sqs_message_body = json.dumps(events)
117+
logger.info(f"total message length:{len(sqs_message_body)}")
118+
119+
sqs_client.send_message(QueueUrl=QUEUE_URL, MessageBody=sqs_message_body, MessageGroupId=filename_key)
121120

122121

123122
if __name__ == "__main__":

0 commit comments

Comments
 (0)