diff --git a/.github/actions/acceptance-tests-components/action.yml b/.github/actions/acceptance-tests-components/action.yml index 0071d20d1..6ba0a788f 100644 --- a/.github/actions/acceptance-tests-components/action.yml +++ b/.github/actions/acceptance-tests-components/action.yml @@ -39,6 +39,18 @@ runs: run: | echo "nodejs_version=$(grep "^nodejs\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT + - name: Set sandbox URL + if: ${{ inputs.testType == 'sandbox' }} + shell: bash + env: + TARGET_ENVIRONMENT: ${{ inputs.targetEnvironment }} + run: | + if [[ "$TARGET_ENVIRONMENT" == pr* ]]; then + echo "SUPPLIER_API_URL_SANDBOX=https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier-PR-${TARGET_ENVIRONMENT#pr}" >> "$GITHUB_ENV" + else + echo "SUPPLIER_API_URL_SANDBOX=https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier" >> "$GITHUB_ENV" + fi + - name: Run test - ${{ inputs.testType }} shell: bash env: diff --git a/.github/actions/build-proxies/action.yml b/.github/actions/build-proxies/action.yml index 5563a3332..2bbb4ee9b 100644 --- a/.github/actions/build-proxies/action.yml +++ b/.github/actions/build-proxies/action.yml @@ -25,10 +25,6 @@ inputs: runId: description: "GitHub Actions run ID to fetch the OAS artifact from" required: true - buildSandbox: - description: "Whether to build the sandbox OAS spec" - required: false - default: false targetComponent: description: "Name of the Component to deploy" required: true @@ -67,7 +63,7 @@ runs: set -euo pipefail ENV="${{ inputs.apimEnv }}" - if [[ "$ENV" == "internal-dev" || "$ENV" == "internal-dev-pr" || "$ENV" == "internal-dev-sandbox" ]]; then + if [[ "$ENV" == "internal-dev" || "$ENV" == "internal-dev-pr" ]]; then echo "TARGET_DOMAIN=suppliers.dev.nhsnotify.national.nhs.uk" >> $GITHUB_ENV elif [[ "$ENV" == "int" || "$ENV" == "ref" ]]; then echo "TARGET_DOMAIN=suppliers.nonprod.nhsnotify.national.nhs.uk" >> $GITHUB_ENV @@ -112,8 +108,7 @@ runs: --targetWorkflow "proxy-deploy.yaml" \ --targetEnvironment "${{ inputs.environment }}" \ --runId "${{ inputs.runId }}" \ - --buildSandbox ${{ inputs.buildSandbox }} \ --apimEnvironment "${{ env.APIM_ENV }}" \ --boundedContext "notify-supplier" \ --targetDomain "$TARGET_DOMAIN" \ - --version "${{ inputs.version }}" + --version "${{ inputs.version }}" \ diff --git a/.github/actions/build-sandbox/action.yml b/.github/actions/build-sandbox/action.yml index c945d06e3..3f187a880 100644 --- a/.github/actions/build-sandbox/action.yml +++ b/.github/actions/build-sandbox/action.yml @@ -3,61 +3,88 @@ description: "Build Sandbox" inputs: version: description: "Version number" + required: false + releaseVersion: + description: "Release, tag, branch, or commit ID to be used for deployment" required: true - - NODE_AUTH_TOKEN: - description: "Token for access to github package registry" + isRelease: + description: "True if releaseVersion is a release tag (if set, downloads from release assets instead of workflow artifacts)" + required: false + default: false + environment: + description: "Deployment environment" + required: true + apimEnv: + description: | + OAS build configuration name (e.g. internal-dev-sandbox, int, prod). + Must match the apimEnv used to build the OAS artifact this action downloads. + The '-pr' suffix is stripped before dispatching to proxygen, which only knows + real APIM environments (internal-dev, internal-dev-sandbox, int, ref, prod). + required: true + runId: + description: "GitHub Actions run ID to fetch the OAS artifact from" + required: true + buildSandbox: + description: "Whether to build the sandbox OAS spec" + required: false + default: false + targetComponent: + description: "Name of the Component to deploy" required: true + default: 'api' + runs: using: composite steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - name: Download Sandbox OAS Spec artifact from workflow + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - node-version: 22 - registry-url: 'https://npm.pkg.github.com' - - - name: Npm install - working-directory: . - env: - NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} - run: npm ci - shell: bash + name: api-oas-specification-internal-dev-sandbox${{ inputs.version != '' && format('-{0}', inputs.version) || '' }} + path: ./build-sandbox - - name: Install Proxygen client + - name: Setup Proxy Name and target shell: bash run: | - # Install proxygen cli - pip install pipx - pipx install proxygen-cli + set -euo pipefail - # Setup proxygen auth and settings - mkdir -p ${HOME}/.proxygen - echo -n $PROXYGEN_PRIVATE_KEY | base64 --decode > ${HOME}/.proxygen/key - envsubst < ./.github/proxygen-credentials-template.yaml > ${HOME}/.proxygen/credentials.yaml - envsubst < ./.github/proxygen-credentials-template.yaml | cat - envsubst < ./.github/proxygen-settings.yaml > ${HOME}/.proxygen/settings.yaml - envsubst < ./.github/proxygen-settings.yaml | cat + ENV="${{ inputs.apimEnv }}" + if [[ "$ENV" == "internal-dev-sandbox" ]]; then + echo "TARGET_DOMAIN=suppliers.dev.nhsnotify.national.nhs.uk" >> $GITHUB_ENV + elif [[ "$ENV" == "int" || "$ENV" == "ref" ]]; then + echo "TARGET_DOMAIN=suppliers.nonprod.nhsnotify.national.nhs.uk" >> $GITHUB_ENV + elif [[ "$ENV" == "prod" ]]; then + echo "TARGET_DOMAIN=suppliers.prod.nhsnotify.national.nhs.uk" >> $GITHUB_ENV + else + echo "[ERROR] Unsupported apimEnv '$ENV'." + exit 1 + fi - - name: Setup Sandbox tag + - name: Set APIM_ENV shell: bash run: | - if [ -z $PR_NUMBER ] - then - echo "SANDBOX_TAG=latest" >> $GITHUB_ENV - else - echo "SANDBOX_TAG=pr$PR_NUMBER" >> $GITHUB_ENV - fi + echo "APIM_ENV=${{ inputs.apimEnv }}" >> "$GITHUB_ENV" - - name: Build and publish sandbox Docker image + - name: Upload Sandbox OAS Spec + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ env.APIM_ENV }}-build-output-sandbox + path: ./build-sandbox + + - name: Trigger deploy sandbox + env: + APP_CLIENT_ID: ${{ env.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ env.APP_PEM_FILE }} shell: bash - working-directory: ./sandbox run: | - proxygen docker get-login | bash - docker build -t nhs-notify-supplier:$SANDBOX_TAG . - DOCKER_REGISTRY=$(proxygen docker registry | tail -1) - IMAGE_ID=$(docker images -q nhs-notify-supplier:$SANDBOX_TAG) - docker tag $IMAGE_ID $DOCKER_REGISTRY/nhs-notify-supplier:$SANDBOX_TAG - docker push $DOCKER_REGISTRY/nhs-notify-supplier:$SANDBOX_TAG + .github/scripts/dispatch_internal_repo_workflow.sh \ + --infraRepoName "nhs-notify-supplier-api" \ + --releaseVersion "${{ inputs.releaseVersion }}" \ + --targetComponent "${{ inputs.targetComponent }}" \ + --targetWorkflow "proxy-sandbox-deploy.yaml" \ + --targetEnvironment "${{ inputs.environment }}" \ + --runId "${{ inputs.runId }}" \ + --apimEnvironment "${{ env.APIM_ENV }}" \ + --boundedContext "notify-supplier" \ + --targetDomain "$TARGET_DOMAIN" \ + --version "${{ inputs.version }}" \ diff --git a/.github/actions/test-types.json b/.github/actions/test-types.json index 6ae115da8..fe18d5da7 100644 --- a/.github/actions/test-types.json +++ b/.github/actions/test-types.json @@ -1,4 +1,5 @@ [ "component", - "e2e" + "e2e", + "sandbox" ] diff --git a/.github/workflows/stage-2-test.yaml b/.github/workflows/stage-2-test.yaml index 241fa84e8..77914d22e 100644 --- a/.github/workflows/stage-2-test.yaml +++ b/.github/workflows/stage-2-test.yaml @@ -45,7 +45,7 @@ jobs: check-generated-dependencies: name: "Check generated dependencies" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 steps: - name: "Checkout code" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -65,7 +65,6 @@ jobs: - name: "Repo setup" uses: ./.github/actions/node-install with: - node-version: ${{ inputs.nodejs_version }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | @@ -74,7 +73,7 @@ jobs: test-unit: name: "Unit tests" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -96,7 +95,6 @@ jobs: - name: "Repo setup" uses: ./.github/actions/node-install with: - node-version: ${{ inputs.nodejs_version }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | @@ -119,7 +117,7 @@ jobs: test-pact: name: "Pact tests" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} permissions: @@ -139,7 +137,6 @@ jobs: - name: "Repo setup" uses: ./.github/actions/node-install with: - node-version: ${{ inputs.nodejs_version }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: npm run generate-dependencies --workspaces --if-present @@ -150,7 +147,7 @@ jobs: test-lint: name: "Linting" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -172,7 +169,6 @@ jobs: - name: "Repo setup" uses: ./.github/actions/node-install with: - node-version: ${{ inputs.nodejs_version }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | @@ -183,7 +179,7 @@ jobs: test-typecheck: name: "Typecheck" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -205,7 +201,6 @@ jobs: - name: "Repo setup" uses: ./.github/actions/node-install with: - node-version: ${{ inputs.nodejs_version }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | diff --git a/.github/workflows/stage-3-build.yaml b/.github/workflows/stage-3-build.yaml index 82abd3c87..e47e9e8fc 100644 --- a/.github/workflows/stage-3-build.yaml +++ b/.github/workflows/stage-3-build.yaml @@ -118,7 +118,7 @@ jobs: artefact-oas-spec: name: "OAS spec ready" runs-on: ubuntu-latest - needs: [artefact-oas-spec-pr, artefact-oas-spec-main] + needs: [artefact-oas-spec-pr, artefact-oas-spec-main, artefact-oas-spec-sandbox] if: always() && !failure() steps: - run: echo "OAS spec build complete" @@ -237,5 +237,12 @@ jobs: environment: ${{ needs.create-dynamic-environment.outputs.environment_name }} apimEnv: "${{ inputs.pr_number == '' && 'internal-dev' || 'internal-dev-pr' }}" runId: "${{ github.run_id }}" - buildSandbox: true + releaseVersion: ${{ github.head_ref || github.ref_name }} + - name: "Build sandbox" + uses: ./.github/actions/build-sandbox + with: + version: "${{ inputs.version }}" + environment: ${{ needs.create-dynamic-environment.outputs.environment_name }} + apimEnv: "internal-dev-sandbox" + runId: "${{ github.run_id }}" releaseVersion: ${{ github.head_ref || github.ref_name }} diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index 098f7451b..791978dfd 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -80,4 +80,4 @@ jobs: --targetEnvironment "$ENVIRONMENT" \ --targetAccountGroup "nhs-notify-supplier-api-dev" \ --targetComponent "api" \ - --extraSecretNames '["/dev/e2e/keys/apim/main","/dev/e2e/keys/apim/pr","/dev/e2e/keys/apim/pr/secondary","/dev/e2e/keys/apim/status","/dev/e2e/keys/private"]' + --extraSecretNames '["/dev/e2e/keys/apim/main","/dev/e2e/keys/apim/pr","/dev/e2e/keys/apim/pr/secondary","/dev/e2e/keys/apim/status","/dev/e2e/keys/private"]' \ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..85e502778 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.22.0 diff --git a/sandbox/package.json b/sandbox/package.json index 87c6b6fd4..f8892fde8 100644 --- a/sandbox/package.json +++ b/sandbox/package.json @@ -18,10 +18,15 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "eslint": "^9.27.0", - "eslint-config-airbnb-base": "^14.0.0", + "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.17.2", "mocha": "^11.8.0" }, + "overrides": { + "eslint-config-airbnb-base": { + "eslint": "^9.27.0" + } + }, "eslintConfig": { "env": { "node": true diff --git a/specification/api/components/schemas/apiGatewayError.yml b/specification/api/components/schemas/apiGatewayError.yml index a36048f67..ac1de3f8b 100644 --- a/specification/api/components/schemas/apiGatewayError.yml +++ b/specification/api/components/schemas/apiGatewayError.yml @@ -1,9 +1,6 @@ +type: object description: API gateway error response -content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "Bad Gateway" +properties: + message: + type: string + example: Bad Gateway diff --git a/tests/README.md b/tests/README.md index cf34ec667..fe2bcdeba 100644 --- a/tests/README.md +++ b/tests/README.md @@ -24,7 +24,12 @@ Test suites that validate the supplier API beyond individual package unit tests. 2. In the root level create an `.env` file and setup the `GITHUB_TOKEN` and `TARGET_ENVIRONMENT` variables (use `.env.template` as a guide) 3. Source the env file by running `set -a` -> `source .env` -> `set +a` 4. Login to your AWS account by running `aws sso login` in the terminal -- **Sandbox tests** require a sandbox server. +- **Sandbox tests**: We need to start the sandbox server from the `/sandbox` folder in the root directory (read /sandbox/README.md for more information). + - Run locally: + 1. from project's root directory `cd sandbox && npm install && npm start` to start the sandbox server + 2. from project's root directory `make test-sandbox` or `cd tests && npm install && npm run test:sandbox` to run the tests + - Run in Pipeline: + 1. You need to deploy the proxies in your Pull Request with the label `deploy-proxy`. This should then build the sandbox server, deploy the artifact and deploy the sandbox proxy via proxygen (you might need to re-run the acceptance tests stage in the pipeline, as the server might take some time to deploy and be unreachable from the tests). - **Performance and E2E tests** require AWS credentials, deployed infrastructure, and seeded test data. See `scripts/test-data/` for test data generation and `tests/e2e-tests/README.md` for environment-specific setup. 1. Deploy a dynamic environment (can be achieved by creating a Pull Request). Take a note of the environment e.g. pr1234. 2. Build proxies in the dynamic environment by setting the label `deploy_proxy` (ask a member of the team if you need help) diff --git a/tests/constants/api-constants.ts b/tests/constants/api-constants.ts index f2d9df297..72efefa5a 100644 --- a/tests/constants/api-constants.ts +++ b/tests/constants/api-constants.ts @@ -1,6 +1,7 @@ export const SUPPLIER_LETTERS = "letters"; +const DEFAULT_SUPPLIER_API_URL_SANDBOX = "http://127.0.0.1:9000"; export const SUPPLIER_API_URL_SANDBOX = - "https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier"; + process.env.SUPPLIER_API_URL_SANDBOX ?? DEFAULT_SUPPLIER_API_URL_SANDBOX; export const AWS_REGION = "eu-west-2"; export const envName = process.env.TARGET_ENVIRONMENT ?? "main"; export const API_NAME = `nhs-${envName}-supapi`; diff --git a/tests/sandbox/testCases/get-letter-status-test-cases.ts b/tests/sandbox/testCases/get-letter-status-test-cases.ts index 1e039b922..4f8ec2808 100644 --- a/tests/sandbox/testCases/get-letter-status-test-cases.ts +++ b/tests/sandbox/testCases/get-letter-status-test-cases.ts @@ -29,6 +29,7 @@ type GetLetterData = { attributes: { specificationId: string; groupId: string; + sha256Hash: string; status: string; }; }; @@ -39,6 +40,7 @@ type RejectedLetterData = { attributes: { specificationId: string; groupId: string; + sha256Hash: string; status: string; reasonCode: string; reasonText: string; @@ -74,6 +76,8 @@ export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCas attributes: { specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", groupId: "c5d93f917f5546d08beccf770a915d96", + sha256Hash: + "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", status: "ACCEPTED", }, }, @@ -91,6 +95,8 @@ export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCas attributes: { specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", groupId: "c5d93f917f5546d08beccf770a915d96", + sha256Hash: + "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", status: "REJECTED", reasonCode: "R01", reasonText: "failed validation", @@ -110,6 +116,8 @@ export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCas attributes: { specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", groupId: "c5d93f917f5546d08beccf770a915d96", + sha256Hash: + "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", status: "CANCELLED", reasonCode: "R01", }, diff --git a/tests/sandbox/testCases/get-list-of-letters-test-cases.ts b/tests/sandbox/testCases/get-list-of-letters-test-cases.ts index b41534b37..c26daae04 100644 --- a/tests/sandbox/testCases/get-list-of-letters-test-cases.ts +++ b/tests/sandbox/testCases/get-list-of-letters-test-cases.ts @@ -22,6 +22,7 @@ type ApiData = { attributes: { specificationId: string; groupId: string; + sha256Hash: string; status: string; }; }; @@ -59,6 +60,8 @@ export const apiSandboxGetLettersRequestTestData: ApiSandboxGetLettersRequestTes attributes: { specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", groupId: "c5d93f917f5546d08beccf770a915d96", + sha256Hash: + "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", status: "PENDING", }, },