diff --git a/.github/workflows/preview-env.yml b/.github/workflows/preview-env.yml index a1a6750e..d3a4febd 100644 --- a/.github/workflows/preview-env.yml +++ b/.github/workflows/preview-env.yml @@ -155,6 +155,56 @@ jobs: ECS_CLUSTER=$(jq -r '.ecs_cluster_name.value' tf-output.json) echo "ecs_cluster=$ECS_CLUSTER" >> $GITHUB_OUTPUT + - name: Install yq for YAML template processing + run: | + sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq + + - name: Inject secrets into openapi.yaml for deploying proxy + if: github.event.action != 'closed' + working-directory: gateway-api + env: + MTLS_SECRET_KEY: ${{ secrets.MTLS_SECRET_KEY }} + PREVIEW_URL: ${{ steps.tf-output.outputs.preview_url }} + run: | + cp openapi.template.yaml openapi.yaml + + yq eval '.x-nhsd-apim.target.url = env(PREVIEW_URL) | .x-nhsd-apim.target.security.secret = env(MTLS_SECRET_KEY)' -i openapi.yaml + + - name: Install proxygen-cli + run: | + pip install proxygen-cli + proxygen --version + + - name: Get proxygen machine user details + id: proxygen-machine-user + uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 + with: + secret-ids: | + /cds/gateway/dev/proxygen/proxygen-key-secret + name-transformation: lowercase + + - name: Apply proxygen details + working-directory: proxygen + run: | + cp settings.yaml $HOME/.proxygen/settings.yaml + + printf "%s" "$_cds_gateway_dev_proxygen_proxygen_key_secret" > /tmp/proxygen_private_key.pem + cp credentials.template.yaml $HOME/.proxygen/credentials.yaml + yq eval '.private_key_path = "/tmp/proxygen_private_key.pem"' -i $HOME/.proxygen/credentials.yaml + + - name: Deploy preview API proxy + if: github.event.action != 'closed' + working-directory: gateway-api + run: | + proxygen instance deploy internal-dev "clinical-data-gateway-api-poc-pr-${{ github.event.pull_request.number }}" openapi.yaml --no-confirm + + + - name: Tear down preview API proxy + if: github.event.action == 'closed' + run: | + proxygen instance delete internal-dev clinical-data-gateway-api-poc-pr-${{ github.event.pull_request.number }} --no-confirm + # ---------- Ensure re-deployment (PR updated) ---------- - name: Force ECS service redeployment if: github.event.action == 'synchronize' @@ -263,6 +313,7 @@ jobs: script: | const alb = '${{ steps.tf-output.outputs.target_group }}'; const url = '${{ steps.tf-output.outputs.preview_url }}'; + const proxy_url = 'https://internal-dev.api.service.nhs.uk/clinical-data-gateway-api-poc-pr-${{ github.event.pull_request.number }}'; const cluster = '${{ steps.tf-output.outputs.ecs_cluster }}'; const service = '${{ steps.tf-output.outputs.ecs_service }}'; const owner = context.repo.owner; @@ -303,6 +354,7 @@ jobs: const lines = [ '**Deployment Complete**', `- Preview URL: [${url}](${url}) — [Health endpoint](${url}/health)`, + `- Proxy URL: [${proxy_url}](${proxy_url})`, `- Smoke Test: ${smokeReadable} (HTTP ${smokeStatus})`, `- ECS Cluster: \`${cluster}\``, `- ECS Service: \`${service}\``, diff --git a/gateway-api/openapi.template.yaml b/gateway-api/openapi.template.yaml new file mode 100644 index 00000000..e34347cd --- /dev/null +++ b/gateway-api/openapi.template.yaml @@ -0,0 +1,183 @@ +openapi: 3.0.3 +info: + title: Gateway API + description: Clinical Data Gateway API + version: 0.1.0 + contact: + name: API Support +servers: + - url: http://localhost:5000 + description: Local development server +components: + securitySchemes: + app-level0: + $ref: https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/app-level0 +paths: + /patient/$gpc.getstructuredrecord: + post: + summary: Get structured record + description: Returns a FHIR Bundle containing patient structured record + security: + - app-level0: [] + operationId: getStructuredRecord + parameters: + - in: header + name: Content-Type + schema: + type: string + enum: [application/fhir+json] + required: true + requestBody: + required: true + content: + application/fhir+json: + schema: + type: object + properties: + resourceType: + type: string + example: "Parameters" + parameter: + type: array + items: + type: object + properties: + name: + type: string + example: "patientNHSNumber" + valueIdentifier: + type: object + properties: + system: + type: string + example: "https://fhir.nhs.uk/Id/nhs-number" + value: + type: string + example: "9999999999" + responses: + '200': + description: Successful response + parameters: + - in: header + name: Content-Type + schema: + type: string + enum: [application/fhir+json] + required: true + content: + application/fhir+json: + schema: + type: object + properties: + statusCode: + type: integer + description: Status code of the interaction + example: 200 + headers: + type: object + properties: + Content-Type: + type: string + example: "application/fhir+json" + body: + type: object + description: FHIR Bundle containing patient data + properties: + resourceType: + type: string + example: "Bundle" + id: + type: string + example: "example-patient-bundle" + type: + type: string + example: "collection" + timestamp: + type: string + format: date-time + example: "2026-01-12T10:00:00Z" + entry: + type: array + items: + type: object + properties: + fullUrl: + type: string + example: "urn:uuid:123e4567-e89b-12d3-a456-426614174000" + resource: + type: object + properties: + resourceType: + type: string + example: "Patient" + id: + type: string + example: "9999999999" + identifier: + type: array + items: + type: object + properties: + system: + type: string + example: "https://fhir.nhs.uk/Id/nhs-number" + value: + type: string + example: "9999999999" + name: + type: array + items: + type: object + properties: + use: + type: string + example: "official" + family: + type: string + example: "Doe" + given: + type: array + items: + type: string + example: ["John"] + gender: + type: string + example: "male" + birthDate: + type: string + format: date + example: "1985-04-12" + /health: + get: + summary: Health check + description: Returns the health status of the API + security: + - app-level0: [] + operationId: healthCheck + responses: + '200': + description: Service is healthy + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "healthy" + required: + - status + +x-nhsd-apim: + monitoring: false + access: + - title: Application Restricted + grants: + app-level0: [] + target: + type: external + healthcheck: /health + url: + security: + type: mtls + secret: diff --git a/proxygen/credentials.template.yaml b/proxygen/credentials.template.yaml new file mode 100644 index 00000000..3a607e83 --- /dev/null +++ b/proxygen/credentials.template.yaml @@ -0,0 +1,7 @@ +base_url: https://identity.prod.api.platform.nhs.uk/realms/api-producers +client_id: clinical-data-gateway-api-poc-client +client_secret: '' +key_id: poc-cli-key-1 +password: '' +private_key_path: +username: '' diff --git a/proxygen/settings.yaml b/proxygen/settings.yaml new file mode 100644 index 00000000..b3d0b3b7 --- /dev/null +++ b/proxygen/settings.yaml @@ -0,0 +1,3 @@ +api: clinical-data-gateway-api-poc +endpoint_url: https://proxygen.prod.api.platform.nhs.uk +spec_output_format: yaml