Skip to content

Commit a10c914

Browse files
New: [AEA-6037] - Introduce Proxygen API for PFP (#2254)
## Summary - ✨ New Feature ### Details - Introduce PFP Proxygen API by utilising OAS specification from https://github.com/NHSDigital/prescriptions-for-patients - Build out deployment pipeline to automatically deploy to Apigee - Introduce required change to PFP to work with new proxygen headers --------- Signed-off-by: Connor Avery <[email protected]> Co-authored-by: Tim Stephenson <[email protected]>
1 parent c8ac0f9 commit a10c914

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+7161
-248
lines changed

.devcontainer/Dockerfile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,16 @@ RUN if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" = "aarch64" ]; then \
4444

4545
# Install ASDF
4646
RUN ASDF_VERSION=$(awk '!/^#/ && NF {print $1; exit}' /tmp/.tool-versions.asdf) && \
47-
wget -O /tmp/asdf.tar.gz https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-amd64.tar.gz; \
48-
tar -xvzf /tmp/asdf.tar.gz; \
49-
mv asdf /usr/bin
47+
if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then \
48+
wget -O /tmp/asdf.tar.gz "https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-arm64.tar.gz"; \
49+
else \
50+
wget -O /tmp/asdf.tar.gz "https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-amd64.tar.gz"; \
51+
fi && \
52+
tar -xzf /tmp/asdf.tar.gz -C /tmp && \
53+
mkdir -p /usr/bin && \
54+
mv /tmp/asdf /usr/bin/asdf && \
55+
chmod +x /usr/bin/asdf && \
56+
rm -rf /tmp/asdf.tar.gz
5057

5158
# specify DOCKER_GID to force container docker group id to match host
5259
RUN if [ -n "${DOCKER_GID}" ]; then \

.github/scripts/deploy_api.sh

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
#!/usr/bin/env bash
2+
set -eu pipefail
3+
4+
echo "Specification path: ${SPEC_PATH}"
5+
echo "Specification version: ${VERSION_NUMBER}"
6+
echo "Stack name: ${STACK_NAME}"
7+
echo "AWS environment: ${AWS_ENVIRONMENT}"
8+
echo "Apigee environment: ${APIGEE_ENVIRONMENT}"
9+
echo "Proxygen private key name: ${PROXYGEN_PRIVATE_KEY_NAME}"
10+
echo "Proxygen KID: ${PROXYGEN_KID}"
11+
echo "Dry run: ${DRY_RUN}"
12+
echo "ENABLE_MUTUAL_TLS: ${ENABLE_MUTUAL_TLS}"
13+
echo "is_pull_request: ${IS_PULL_REQUEST}"
14+
15+
client_private_key=$(cat ~/.proxygen/tmp/client_private_key)
16+
client_cert=$(cat ~/.proxygen/tmp/client_cert)
17+
18+
if [ -z "${client_private_key}" ]; then
19+
echo "client_private_key is unset or set to the empty string"
20+
exit 1
21+
fi
22+
if [ -z "${client_cert}" ]; then
23+
echo "client_cert is unset or set to the empty string"
24+
exit 1
25+
fi
26+
27+
put_secret_lambda=lambda-resources-ProxygenPTLMTLSSecretPut
28+
instance_put_lambda=lambda-resources-ProxygenPTLInstancePut
29+
spec_publish_lambda=lambda-resources-ProxygenPTLSpecPublish
30+
31+
if [[ "$APIGEE_ENVIRONMENT" =~ ^(int|sandbox|prod)$ ]]; then
32+
put_secret_lambda=lambda-resources-ProxygenProdMTLSSecretPut
33+
instance_put_lambda=lambda-resources-ProxygenProdInstancePut
34+
spec_publish_lambda=lambda-resources-ProxygenProdSpecPublish
35+
fi
36+
37+
instance_suffix=""
38+
if [[ "${IS_PULL_REQUEST}" == "true" ]]; then
39+
# Extracting the PR ID from $STACK_NAME
40+
pr_id=$(echo "$STACK_NAME" | awk -F'-' '{print $NF}')
41+
instance_suffix=-"pr-${pr_id}"
42+
fi
43+
44+
# Determine the proxy instance based on the provided $STACK_NAME
45+
apigee_api=prescriptions-for-patients-proxygen
46+
apigee_client=prescriptions-for-patients-proxygen
47+
instance="pfp-proxygen${instance_suffix}"
48+
49+
echo "Proxy instance: ${instance}"
50+
echo "Apigee api: ${apigee_api}"
51+
echo "Apigee client: ${apigee_client}"
52+
53+
echo
54+
55+
echo "Fixing the spec"
56+
# Find and replace the title
57+
title=$(jq -r '.info.title' "${SPEC_PATH}")
58+
if [[ "${IS_PULL_REQUEST}" == "true" ]]; then
59+
jq --arg title "[PR-${pr_id}] $title" '.info.title = $title' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
60+
echo "disabling monitoring for pull request deployment"
61+
jq '."x-nhsd-apim".monitoring = false' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
62+
fi
63+
64+
# Find and replace the specification version number
65+
jq --arg version "${VERSION_NUMBER}" '.info.version = $version' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
66+
67+
# Find and replace the x-nhsd-apim.target.url value
68+
jq --arg stack_name "${STACK_NAME}" --arg aws_env "${AWS_ENVIRONMENT}" '.["x-nhsd-apim"].target.url = "https://\($stack_name).\($aws_env).eps.national.nhs.uk"' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
69+
70+
# Find and replace the servers object
71+
if [[ "${APIGEE_ENVIRONMENT}" == "prod" ]]; then
72+
jq --arg inst "${instance}" '.servers = [ { "url": "https://api.service.nhs.uk/\($inst)" } ]' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
73+
else
74+
jq --arg env "${APIGEE_ENVIRONMENT}" --arg inst "${instance}" '.servers = [ { "url": "https://\($env).api.service.nhs.uk/\($inst)" } ]' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
75+
fi
76+
77+
# Find and replace securitySchemes
78+
if [[ "${APIGEE_ENVIRONMENT}" == "prod" ]]; then
79+
jq '.components.securitySchemes."nhs-cis2-aal3" = {"$ref": "https://proxygen.prod.api.platform.nhs.uk/components/securitySchemes/nhs-cis2-aal3"}' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
80+
jq '.components.securitySchemes."nhs-login-p9" = {"$ref": "https://proxygen.prod.api.platform.nhs.uk/components/securitySchemes/nhs-login-p9"}' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
81+
else
82+
jq '.components.securitySchemes."nhs-cis2-aal3" = {"$ref": "https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/nhs-cis2-aal3"}' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
83+
jq '.components.securitySchemes."nhs-login-p9" = {"$ref": "https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/nhs-login-p9"}' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
84+
fi
85+
86+
# Find and replace the x-nhsd-apim.target.secret value
87+
jq --arg mtls_key "${MTLS_KEY}" '.["x-nhsd-apim"].target.security.secret = "\($mtls_key)"' "${SPEC_PATH}" > temp.json && mv temp.json "${SPEC_PATH}"
88+
89+
# Remove target attributes if the environment is sandbox
90+
if [[ "${APIGEE_ENVIRONMENT}" == *"sandbox"* ]]; then
91+
echo "Removing target attributes for sandbox environment"
92+
jq 'del(."x-nhsd-apim"."target-attributes")' "$SPEC_PATH" > temp.json && mv temp.json "${SPEC_PATH}"
93+
fi
94+
95+
echo
96+
97+
echo "Retrieving proxygen credentials"
98+
99+
# Retrieve the proxygen private key and client private key and cert from AWS Secrets Manager
100+
proxygen_private_key_arn=$(aws cloudformation list-exports --query "Exports[?Name=='secrets:${PROXYGEN_PRIVATE_KEY_NAME}'].Value" --output text)
101+
102+
if [[ "${ENABLE_MUTUAL_TLS}" == "true" ]]; then
103+
echo
104+
echo "Store the secret used for mutual TLS to AWS using Proxygen proxy lambda"
105+
if [[ "${DRY_RUN}" == "false" ]]; then
106+
jq -n --arg apiName "${apigee_api}" \
107+
--arg apiClient "${apigee_client}" \
108+
--arg environment "${APIGEE_ENVIRONMENT}" \
109+
--arg secretName "${MTLS_KEY}" \
110+
--arg secretKey "${client_private_key}" \
111+
--arg secretCert "${client_cert}" \
112+
--arg kid "${PROXYGEN_KID}" \
113+
--arg proxygenSecretName "${proxygen_private_key_arn}" \
114+
'{apiName: $apiName, apiClient: $apiClient, environment: $environment, secretName: $secretName, secretKey: $secretKey, secretCert: $secretCert, kid, $kid, proxygenSecretName: $proxygenSecretName}' > payload.json
115+
116+
aws lambda invoke --function-name "${put_secret_lambda}" --cli-binary-format raw-in-base64-out --payload file://payload.json out.txt > response.json
117+
if eval "cat response.json | jq -e '.FunctionError' >/dev/null"; then
118+
echo 'Error calling lambda'
119+
cat out.txt
120+
exit 1
121+
fi
122+
echo "Secret stored successfully"
123+
else
124+
echo "Would call ${put_secret_lambda}"
125+
fi
126+
fi
127+
128+
echo
129+
echo "Deploy the API instance using Proxygen proxy lambda"
130+
if [[ "${DRY_RUN}" == "false" ]]; then
131+
132+
jq -n --argfile spec "${SPEC_PATH}" \
133+
--arg apiName "${apigee_api}" \
134+
--arg apiClient "${apigee_client}" \
135+
--arg environment "${APIGEE_ENVIRONMENT}" \
136+
--arg instance "${instance}" \
137+
--arg kid "${PROXYGEN_KID}" \
138+
--arg proxygenSecretName "${proxygen_private_key_arn}" \
139+
'{apiName: $apiName, apiClient: $apiClient, environment: $environment, specDefinition: $spec, instance: $instance, kid: $kid, proxygenSecretName: $proxygenSecretName}' > payload.json
140+
141+
aws lambda invoke --function-name "${instance_put_lambda}" --cli-binary-format raw-in-base64-out --payload file://payload.json out.txt > response.json
142+
143+
if eval "cat response.json | jq -e '.FunctionError' >/dev/null"; then
144+
echo 'Error calling lambda'
145+
cat out.txt
146+
exit 1
147+
fi
148+
echo "Instance deployed"
149+
else
150+
echo "Would call ${instance_put_lambda}"
151+
fi
152+
153+
# if [[ "${APIGEE_ENVIRONMENT}" == "int" ]]; then
154+
# echo
155+
# echo "Deploy the API spec to prod catalogue as it is int environment"
156+
# if [[ "${DRY_RUN}" == "false" ]]; then
157+
# jq -n --argfile spec "${SPEC_PATH}" \
158+
# --arg apiName "${apigee_api}" \
159+
# --arg apiClient "${apigee_client}" \
160+
# --arg environment "prod" \
161+
# --arg instance "${instance}" \
162+
# --arg kid "${PROXYGEN_KID}" \
163+
# --arg proxygenSecretName "${proxygen_private_key_arn}" \
164+
# '{apiName: $apiName, apiClient: $apiClient, environment: $environment, specDefinition: $spec, instance: $instance, kid: $kid, proxygenSecretName: $proxygenSecretName}' > payload.json
165+
166+
# aws lambda invoke --function-name "${spec_publish_lambda}" --cli-binary-format raw-in-base64-out --payload file://payload.json out.txt > response.json
167+
168+
# if eval "cat response.json | jq -e '.FunctionError' >/dev/null"; then
169+
# echo 'Error calling lambda'
170+
# cat out.txt
171+
# exit 1
172+
# fi
173+
# echo "Spec deployed"
174+
# else
175+
# echo "Would call ${spec_publish_lambda}"
176+
# fi
177+
# fi
178+
179+
if [[ "${APIGEE_ENVIRONMENT}" == "internal-dev" && "${IS_PULL_REQUEST}" == "false" ]]; then
180+
echo
181+
echo "Deploy the API spec to uat catalogue as it is internal-dev environment"
182+
if [[ "${DRY_RUN}" == "false" ]]; then
183+
jq -n --argfile spec "${SPEC_PATH}" \
184+
--arg apiName "${apigee_api}" \
185+
--arg apiClient "${apigee_client}" \
186+
--arg environment "uat" \
187+
--arg instance "${instance}" \
188+
--arg kid "${PROXYGEN_KID}" \
189+
--arg proxygenSecretName "${proxygen_private_key_arn}" \
190+
'{apiName: $apiName, apiClient: $apiClient, environment: $environment, specDefinition: $spec, instance: $instance, kid: $kid, proxygenSecretName: $proxygenSecretName}' > payload.json
191+
192+
aws lambda invoke --function-name "${spec_publish_lambda}" --cli-binary-format raw-in-base64-out --payload file://payload.json out.txt > response.json
193+
194+
if eval "cat response.json | jq -e '.FunctionError' >/dev/null"; then
195+
echo 'Error calling lambda'
196+
cat out.txt
197+
exit 1
198+
fi
199+
echo "Spec deployed"
200+
else
201+
echo "Would call ${spec_publish_lambda}"
202+
fi
203+
fi

.github/scripts/release_code.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,5 @@ sam deploy \
5252
ForwardCsocLogs="$FORWARD_CSOC_LOGS" \
5353
TC007NHSNumberValue="$TC007_NHS_NUMBERS" \
5454
TC008NHSNumberValue="$TC008_NHS_NUMBERS" \
55-
TC009NHSNumberValue="$TC009_NHS_NUMBERS"
55+
TC009NHSNumberValue="$TC009_NHS_NUMBERS" \
56+
AllowNHSNumberOverride="$ALLOW_NHS_NUMBER_OVERRIDE"

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
TARGET_ENVIRONMENT: dev
6868
APIGEE_ENVIRONMENT: internal-dev
6969
ENABLE_MUTUAL_TLS: true
70+
MTLS_KEY: prescriptions-for-patients-mtls-1
7071
BUILD_ARTIFACT: packaged_code
7172
TRUSTSTORE_FILE: pfp-truststore.pem
7273
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
@@ -81,6 +82,8 @@ jobs:
8182
RUN_REGRESSION_TESTS: true
8283
REGRESSION_TEST_PRODUCT: PFP-APIGEE
8384
FORWARD_CSOC_LOGS: false
85+
ALLOW_NHS_NUMBER_OVERRIDE: true
86+
REGRESSION_TEST_NON_PROXYGEN: true
8487
secrets:
8588
REGRESSION_TESTS_PEM: ${{ secrets.REGRESSION_TESTS_PEM }}
8689
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
@@ -90,6 +93,7 @@ jobs:
9093
INT_CLOUD_FORMATION_CHECK_VERSION_ROLE: ${{ secrets.INT_CLOUD_FORMATION_CHECK_VERSION_ROLE }}
9194
PROD_CLOUD_FORMATION_CHECK_VERSION_ROLE: ${{ secrets.PROD_CLOUD_FORMATION_CHECK_VERSION_ROLE }}
9295
DEV_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE }}
96+
PROXYGEN_ROLE: ${{ secrets.PROXYGEN_PTL_ROLE }}
9397

9498
release_dev_sandbox:
9599
needs: [tag_release, package_code, get_commit_id]
@@ -100,6 +104,7 @@ jobs:
100104
TARGET_ENVIRONMENT: dev
101105
APIGEE_ENVIRONMENT: internal-dev-sandbox
102106
ENABLE_MUTUAL_TLS: true
107+
MTLS_KEY: prescriptions-for-patients-mtls-1
103108
BUILD_ARTIFACT: packaged_sandbox_code
104109
TRUSTSTORE_FILE: pfp-sandbox-truststore.pem
105110
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
@@ -109,11 +114,13 @@ jobs:
109114
STATE_MACHINE_LOG_LEVEL: ALL
110115
RUN_REGRESSION_TESTS: false
111116
FORWARD_CSOC_LOGS: false
117+
ALLOW_NHS_NUMBER_OVERRIDE: true
112118
secrets:
113119
REGRESSION_TESTS_PEM: ${{ secrets.REGRESSION_TESTS_PEM }}
114120
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
115121
TARGET_SPINE_SERVER: sandbox
116122
TARGET_SERVICE_SEARCH_SERVER: sandbox
123+
PROXYGEN_ROLE: ${{ secrets.PROXYGEN_PTL_ROLE }}
117124

118125
release_qa:
119126
needs:
@@ -131,6 +138,7 @@ jobs:
131138
TARGET_ENVIRONMENT: qa
132139
APIGEE_ENVIRONMENT: internal-qa
133140
ENABLE_MUTUAL_TLS: true
141+
MTLS_KEY: prescriptions-for-patients-mtls-1
134142
BUILD_ARTIFACT: packaged_code
135143
TRUSTSTORE_FILE: pfp-truststore.pem
136144
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
@@ -143,8 +151,11 @@ jobs:
143151
RUN_REGRESSION_TESTS: true
144152
REGRESSION_TEST_PRODUCT: PFP-APIGEE
145153
FORWARD_CSOC_LOGS: false
154+
ALLOW_NHS_NUMBER_OVERRIDE: true
155+
REGRESSION_TEST_NON_PROXYGEN: true
146156
secrets:
147157
REGRESSION_TESTS_PEM: ${{ secrets.REGRESSION_TESTS_PEM }}
148158
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.QA_CLOUD_FORMATION_DEPLOY_ROLE }}
149159
TARGET_SPINE_SERVER: ${{ secrets.QA_TARGET_SPINE_SERVER }}
150160
TARGET_SERVICE_SEARCH_SERVER: ${{ secrets.QA_TARGET_SERVICE_SEARCH_SERVER }}
161+
PROXYGEN_ROLE: ${{ secrets.PROXYGEN_PTL_ROLE }}

.github/workflows/pull_request.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,15 @@ jobs:
100100
needs: [get_issue_number, package_code, get_commit_id]
101101
uses: ./.github/workflows/sam_release_code.yml
102102
with:
103+
IS_PULL_REQUEST: true
103104
STACK_NAME: pfp-pr-${{needs.get_issue_number.outputs.issue_number}}
104105
ARTIFACT_BUCKET_PREFIX: PR-${{needs.get_issue_number.outputs.issue_number}}
105-
TARGET_ENVIRONMENT: dev-pr
106+
TARGET_ENVIRONMENT: dev
106107
APIGEE_ENVIRONMENT: internal-dev
107108
ENABLE_MUTUAL_TLS: false
109+
MTLS_KEY: prescriptions-for-patients-mtls-1
108110
BUILD_ARTIFACT: packaged_code
109-
TRUSTSTORE_FILE: pfp-truststore.pem
111+
TRUSTSTORE_FILE: pfp-truststore-pr.pem
110112
VERSION_NUMBER: PR-${{ needs.get_issue_number.outputs.issue_number }}
111113
COMMIT_ID: ${{ needs.get_commit_id.outputs.commit_id }}
112114
LOG_LEVEL: DEBUG
@@ -117,21 +119,24 @@ jobs:
117119
RUN_REGRESSION_TESTS: true
118120
REGRESSION_TEST_PRODUCT: PFP-AWS
119121
FORWARD_CSOC_LOGS: false
122+
DEPLOY_APIGEE: true
123+
ALLOW_NHS_NUMBER_OVERRIDE: true
120124
secrets:
121125
REGRESSION_TESTS_PEM: ${{ secrets.REGRESSION_TESTS_PEM }}
122126
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
123127
TARGET_SPINE_SERVER: ${{ secrets.DEV_TARGET_SPINE_SERVER }}
124128
TARGET_SERVICE_SEARCH_SERVER: ${{ secrets.DEV_TARGET_SERVICE_SEARCH_SERVER }}
125-
129+
PROXYGEN_ROLE: ${{ secrets.PROXYGEN_PTL_ROLE }}
126130
release_sandbox_code:
127131
needs: [get_issue_number, package_code, get_commit_id]
128132
uses: ./.github/workflows/sam_release_code.yml
129133
with:
130134
STACK_NAME: pfp-pr-${{needs.get_issue_number.outputs.issue_number}}-sandbox
131135
ARTIFACT_BUCKET_PREFIX: PR-sandbox-${{needs.get_issue_number.outputs.issue_number}}
132-
TARGET_ENVIRONMENT: dev-pr
136+
TARGET_ENVIRONMENT: dev
133137
APIGEE_ENVIRONMENT: internal-dev-sandbox
134138
ENABLE_MUTUAL_TLS: false
139+
MTLS_KEY: prescriptions-for-patients-mtls-1
135140
BUILD_ARTIFACT: packaged_sandbox_code
136141
TRUSTSTORE_FILE: pfp-sandbox-truststore.pem
137142
VERSION_NUMBER: PR-${{ needs.get_issue_number.outputs.issue_number }}
@@ -141,8 +146,11 @@ jobs:
141146
STATE_MACHINE_LOG_LEVEL: ALL
142147
RUN_REGRESSION_TESTS: false
143148
FORWARD_CSOC_LOGS: false
149+
DEPLOY_APIGEE: false
150+
ALLOW_NHS_NUMBER_OVERRIDE: true
144151
secrets:
145152
REGRESSION_TESTS_PEM: ${{ secrets.REGRESSION_TESTS_PEM }}
146153
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
147154
TARGET_SPINE_SERVER: sandbox
148155
TARGET_SERVICE_SEARCH_SERVER: sandbox
156+
PROXYGEN_ROLE: ${{ secrets.PROXYGEN_PTL_ROLE }}

0 commit comments

Comments
 (0)