From 3610abd6864e43ba21b889d436a28ec31d63dae5 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:12:33 +0000 Subject: [PATCH 01/22] use epsat to talk proxygen --- .../tool/site/client/src/pages/configPage.tsx | 11 ++++- .../__snapshots__/configPage.spec.tsx.snap | 5 +++ .../site/server/src/routes/config/config.ts | 4 +- .../src/services/communication/eps-client.ts | 44 +++++++++++-------- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index 5fb6eb5170..f537cedca1 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -10,6 +10,7 @@ import SuccessOrFail from "../components/common/successOrFail" interface ConfigFormValues { useSigningMock: boolean + useProxygen: boolean epsPrNumber: string signingPrNumber: string } @@ -21,7 +22,7 @@ interface ConfigResponse { const ConfigPage: React.FC = () => { const {baseUrl} = useContext(AppContext) const [configUpdateSuccess, setConfigUpdateSuccess] = useState(undefined) - const initialValues = {useSigningMock: false, epsPrNumber: "", signingPrNumber: ""} + const initialValues = {useSigningMock: false, epsPrNumber: "", signingPrNumber: "", useProxygen: false} if (configUpdateSuccess !== undefined) { return <> @@ -56,7 +57,13 @@ const ConfigPage: React.FC = () => { Use Signing Mock - {!formik.values.useSigningMock && + + + + Use Signing Mock + + + {!formik.values.useSigningMock && !formik.values.useProxygen &&
+ +
+
+
+
diff --git a/packages/tool/site/server/src/routes/config/config.ts b/packages/tool/site/server/src/routes/config/config.ts index 9f511fa95f..87da3328fc 100644 --- a/packages/tool/site/server/src/routes/config/config.ts +++ b/packages/tool/site/server/src/routes/config/config.ts @@ -8,11 +8,13 @@ export default { const payload = request.payload as { useSigningMock: boolean, epsPrNumber: string, - signingPrNumber: string + signingPrNumber: string, + useProxygen: boolean } setSessionValue("use_signing_mock", payload.useSigningMock, request) setSessionValue("eps_pr_number", payload.epsPrNumber, request) setSessionValue("signing_pr_number", payload.signingPrNumber, request) + setSessionValue("use_proxygen", payload.useProxygen, request) return h.response({success: true}).code(200) } } diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 4879abe47e..8b45d8c477 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -87,39 +87,39 @@ class EpsClient { async makeGetTaskTrackerRequest(query: QueryParams): Promise { const urlSearchParams = getUrlSearchParams(query) - return (await this.makeApiCall("Task", undefined, urlSearchParams)).data + return (await this.makeApiCall("Task", "prescribe", undefined, urlSearchParams)).data } async makePrepareRequest(body: Bundle): Promise { - return (await this.makeApiCall("$prepare", body)).data + return (await this.makeApiCall("$prepare", "prescribe", body)).data } async makeSendRequest(body: Bundle): Promise> { - return await this.getEpsResponse("$process-message", body) + return await this.getEpsResponse("$process-message", "prescribe", body) } async makeSendFhirRequest(body: Bundle): Promise> { - return await this.getEpsResponse("$process-message", body, undefined, true) + return await this.getEpsResponse("$process-message", "prescribe", body, undefined, true) } async makeReleaseRequest(body: Parameters): Promise> { - return await this.getEpsResponse("Task/$release", body) + return await this.getEpsResponse("Task/$release", "dispense", body) } async makeReturnRequest(body: Task): Promise> { - return await this.getEpsResponse("Task", body) + return await this.getEpsResponse("Task", "dispense", body) } async makeWithdrawRequest(body: Task): Promise> { - return await this.getEpsResponse("Task", body) + return await this.getEpsResponse("Task", "dispense", body) } async makeClaimRequest(body: Claim): Promise> { - return await this.getEpsResponse("Claim", body) + return await this.getEpsResponse("Claim", "dispense", body) } async makePingRequest(): Promise { - const basePath = this.getBasePath() + const basePath = this.getBasePath("prescribe") const url = `${CONFIG.apigeeEgressHost}/${basePath}/_ping` return (await axiosInstance.get(url)).data } @@ -127,20 +127,21 @@ class EpsClient { async makeValidateRequest(body: FhirResource): Promise> { const requestId = uuid.v4() // eslint-disable-next-line max-len - const response = await this.makeApiCall("$validate", body, undefined, requestId, {"x-show-validation-warnings": "true"}) + const response = await this.makeApiCall("$validate", "prescribe", body, undefined, requestId, {"x-show-validation-warnings": "true"}) const statusCode = response.status const fhirResponse = response.data return {statusCode, fhirResponse} } async makeConvertRequest(body: FhirResource): Promise { - const response = (await this.makeApiCall("$convert", body)).data + const response = (await this.makeApiCall("$convert", "prescribe", body)).data return typeof response === "string" ? response : JSON.stringify(response, null, 2) } async makeDoseToTextRequest(body: FhirResource): Promise> { const requestId = uuid.v4() - const response = await this.makeApiCall("$dose-to-text", body, undefined, requestId) + const response = await this.makeApiCall( + "$dose-to-text", "prescribe", body, undefined, requestId) const statusCode = response.status const doseToTextResponse = response.data return {statusCode, fhirResponse: doseToTextResponse} @@ -148,29 +149,31 @@ class EpsClient { private async getEpsResponse( endpoint: string, + api: string, body?: FhirResource, params?: URLSearchParams, fhirResponseOnly?: boolean ) { const requestId = uuid.v4() - const response = await this.makeApiCall(endpoint, body, params, requestId) + const response = await this.makeApiCall(endpoint, api, body, params, requestId) const statusCode = response.status const fhirResponse = response.data const spineResponse = fhirResponseOnly ? "" // eslint-disable-next-line max-len - : (await this.makeApiCall(endpoint, body, params, requestId, {"x-raw-response": "true"})).data + : (await this.makeApiCall(endpoint, api, body, params, requestId, {"x-raw-response": "true"})).data return {statusCode, fhirResponse, spineResponse: this.asString(spineResponse)} } private async makeApiCall( path: string, + api: string, body?: unknown, params?: URLSearchParams, requestId?: string, additionalHeaders?: RawAxiosRequestHeaders ): Promise> { - const basePath = this.getBasePath() + const basePath = this.getBasePath(api) const url = `${CONFIG.apigeeEgressHost}/${basePath}/FHIR/R4/${path}` const headers: RawAxiosRequestHeaders = this.getHeaders(requestId) if (additionalHeaders) { @@ -186,11 +189,16 @@ class EpsClient { }) } - protected getBasePath(): string { + protected getBasePath(api: string): string { const prNumber = getSessionValue("eps_pr_number", this.request) + const useProxygen = getSessionValue("use_proxygen", this.request) + let replacementString = "electronic-prescriptions" + if (useProxygen) { + replacementString = api==="prescribing" ? "fhir-prescribing" : "fhir-dispensing" + } return prNumber - ? `electronic-prescriptions-pr-${prNumber}` - : `${CONFIG.basePath}`.replace("eps-api-tool", "electronic-prescriptions") + ? `${replacementString}-pr-${prNumber}` + : `${CONFIG.basePath}`.replace("eps-api-tool", replacementString) } protected getHeaders(requestId: string | undefined): RawAxiosRequestHeaders { From 9aa8ad63c6e43e20c080727a5c26c38eb2b3fdef Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:52:32 +0000 Subject: [PATCH 02/22] fix comparisson --- packages/tool/site/client/src/pages/configPage.tsx | 2 +- .../tool/site/server/src/services/communication/eps-client.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index f537cedca1..89c386e857 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -60,7 +60,7 @@ const ConfigPage: React.FC = () => { - Use Signing Mock + Use Proxygen {!formik.values.useSigningMock && !formik.values.useProxygen && diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 8b45d8c477..aa03897c3a 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -194,7 +194,7 @@ class EpsClient { const useProxygen = getSessionValue("use_proxygen", this.request) let replacementString = "electronic-prescriptions" if (useProxygen) { - replacementString = api==="prescribing" ? "fhir-prescribing" : "fhir-dispensing" + replacementString = api==="prescribe" ? "fhir-prescribing" : "fhir-dispensing" } return prNumber ? `${replacementString}-pr-${prNumber}` From e8c02d5e67b09519993d0f2b527f0f7a768cb81a Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:57:03 +0000 Subject: [PATCH 03/22] update snapshot --- .../client/tests/pages/__snapshots__/configPage.spec.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap index 759c817c4d..d67c09fe47 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap @@ -13,7 +13,7 @@ exports[`Displays config form 1`] = `
-
+
From 09226f5c3fb9e98ecf56a8d0d1fd6a6e1305bfd6 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:06:08 +0000 Subject: [PATCH 04/22] put logging in better place --- .../src/services/communication/eps-client.ts | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index aa03897c3a..297a1b0805 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -20,45 +20,11 @@ import * as Hapi from "@hapi/hapi" import {getSessionValue} from "../session" import {Ping} from "../../routes/health/get-status" import {DosageTranslationArray} from "../../routes/dose-to-text" -import pino from "pino" - -const logger = pino() type QueryParams = Record> const axiosInstance = axios.create() -axiosInstance.interceptors.request.use((request: InternalAxiosRequestConfig) => { - logger.info({ - request: { - headers: request.headers, - url: request.url, - baseURL: request.baseURL, - method: request.method - }}, "making api call") - - return request -}) - -axiosInstance.interceptors.response.use((response: AxiosResponse) => { - logger.info({ - response: { - headers: response.headers, - status: response.status - }}, "successful api call") - - return response -}, (error: AxiosError) => { - logger.error({ - response: { - headers: error.response?.headers, - status: error.response?.status - }}, "unsuccessful api call") - - // let epsat figure out how to deal with errors so just return response - return error.response -}) - const getUrlSearchParams = (query: QueryParams): URLSearchParams => { const urlSearchParams = new URLSearchParams() Object.keys(query).forEach(key => { @@ -83,6 +49,44 @@ class EpsClient { constructor(request: Hapi.Request) { this.request = request + const logger = request.logger + + axiosInstance.interceptors.request.use((request: InternalAxiosRequestConfig) => { + logger.info({ + apiCall: { + request: { + headers: request.headers, + url: request.url, + baseURL: request.baseURL, + method: request.method + }} + }, "making api call") + + return request + }) + + axiosInstance.interceptors.response.use((response: AxiosResponse) => { + logger.info({ + apiCall: { + response: { + headers: response.headers, + status: response.status + } + } + }, "successful api call") + + return response + }, (error: AxiosError) => { + logger.error({ + response: { + headers: error.response?.headers, + status: error.response?.status + }}, "unsuccessful api call") + + // let epsat figure out how to deal with errors so just return response + return error.response + }) + } async makeGetTaskTrackerRequest(query: QueryParams): Promise { From 7df736a8ec3cba8ddacccd3b3193a10732806da5 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:25:51 +0000 Subject: [PATCH 05/22] add a convert path --- packages/specification/fhir-prescribing.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/specification/fhir-prescribing.yaml b/packages/specification/fhir-prescribing.yaml index 201a237424..c668ef62e3 100644 --- a/packages/specification/fhir-prescribing.yaml +++ b/packages/specification/fhir-prescribing.yaml @@ -565,6 +565,22 @@ paths: $ref: examples/spec/errors/example-a-validation-error-missing-field/Response-FhirError.json security: - nhs-cis2-aal3: [] + /FHIR/R4/$convert : + post: + operationId: convert + summary: dummy operation for path + description: | + this is a dummy method so things go to the backend correctly + parameters: + - $ref: "#/components/parameters/BearerAuthorization" + - $ref: "#/components/parameters/RoleId" + - $ref: "#/components/parameters/RequestID" + - $ref: "#/components/parameters/CorrelationID" + responses: + "200": + description: "Dummy response" + security: + - nhs-cis2-aal3: [] components: securitySchemes: From 6ecb5c73d16415802c78016b67f3586b975b7032 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:09:33 +0000 Subject: [PATCH 06/22] set an epsat request id --- .gitallowed | 1 + .../site/client/src/requests/axiosInstance.ts | 20 +++++++++++++++++-- .../__snapshots__/claimPage.spec.tsx.snap | 15 ++++++++------ .../__snapshots__/dispensePage.spec.tsx.snap | 10 ++++++---- .../prescriptionSearchPage.spec.tsx.snap | 10 ++++++---- .../__snapshots__/signPage.spec.tsx.snap | 10 ++++++---- .../client/tests/pages/claimPage.spec.tsx | 4 ++++ .../client/tests/pages/dispensePage.spec.tsx | 4 ++++ .../pages/prescriptionSearchPage.spec.tsx | 4 ++++ .../site/client/tests/pages/signPage.spec.tsx | 4 ++++ .../src/services/communication/eps-client.ts | 13 ++++++------ 11 files changed, 69 insertions(+), 26 deletions(-) diff --git a/.gitallowed b/.gitallowed index 9327600de1..a703f62f6b 100644 --- a/.gitallowed +++ b/.gitallowed @@ -45,6 +45,7 @@ super\(\"1\.2 # snapshots are fine ^[a-f0-9]{40}:?packages\/tool\/site\/client\/tests\/pages\/__snapshots__\/.*snap +^packages\/tool\/site\/client\/tests\/pages\/__snapshots__\/.*snap # files that no longer exist or have been moved ^[a-f0-9]{40}:.*sds-api\/spine-directory-service\/sds\/lookup\/tests\/data\/my_real_server_schema\.json diff --git a/packages/tool/site/client/src/requests/axiosInstance.ts b/packages/tool/site/client/src/requests/axiosInstance.ts index 9e36ed689b..1466311c4c 100644 --- a/packages/tool/site/client/src/requests/axiosInstance.ts +++ b/packages/tool/site/client/src/requests/axiosInstance.ts @@ -1,5 +1,21 @@ -import axios from "axios" +import axios, {InternalAxiosRequestConfig} from "axios" +import {v4 as uuidv4} from "uuid" -export const axiosInstance = axios.create({ +const axiosInstance = axios.create({ validateStatus: () => true }) + +axiosInstance.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const requestId = uuidv4() + console.log(`making epsat request id ${requestId}`) + config.headers["x-epsat-request-id"] = requestId + + return config + }, + (error) => { + return Promise.reject(Error(error)) + } +) + +export {axiosInstance} diff --git a/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap index 9585ffe3e4..10235ee6d8 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap @@ -31,8 +31,9 @@ exports[`Displays an error if prescription-order not found 1`] = `
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
@@ -80,8 +81,9 @@ exports[`Displays an error if previous claim not found for amend 1`] = `
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
@@ -129,8 +131,9 @@ exports[`Displays an error on invalid response 1`] = `
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap index 03050e980a..981f4ca3ec 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap @@ -22,8 +22,9 @@ exports[`Displays an error if prescription-order not found 1`] = `
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
@@ -71,8 +72,9 @@ exports[`Displays an error on invalid response 1`] = `
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap index 0e6363541e..3fbeb7c6c5 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap @@ -160,8 +160,9 @@ exports[`Displays an error message if summary search returns an error 1`] = `
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
@@ -233,8 +234,9 @@ exports[`Displays an error message if summary search returns invalid response 1`
Request Headers
-
{
-  "Accept": "application/json, text/plain, */*"
+        
{
+  "Accept": "application/json, text/plain, */*",
+  "x-epsat-request-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap index c76655dac7..a6513099c5 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap @@ -22,10 +22,11 @@ exports[`Displays error message if prepare errors present 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
   "Content-Type": "application/x-www-form-urlencoded",
-  "nhsd-identity-authentication-method": "N3_SMARTCARD"
+  "nhsd-identity-authentication-method": "N3_SMARTCARD",
+  "x-epsat-request-id": "test-uuid"
 }
@@ -92,10 +93,11 @@ exports[`Displays error message if redirect URI not present 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
   "Content-Type": "application/x-www-form-urlencoded",
-  "nhsd-identity-authentication-method": "N3_SMARTCARD"
+  "nhsd-identity-authentication-method": "N3_SMARTCARD",
+  "x-epsat-request-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/claimPage.spec.tsx b/packages/tool/site/client/tests/pages/claimPage.spec.tsx index 694df0f18b..c9eb11869e 100644 --- a/packages/tool/site/client/tests/pages/claimPage.spec.tsx +++ b/packages/tool/site/client/tests/pages/claimPage.spec.tsx @@ -1,5 +1,6 @@ import {waitFor} from "@testing-library/react" import {screen} from "@testing-library/dom" +import {v4} from "uuid" import pretty from "pretty" import * as React from "react" import MockAdapter from "axios-mock-adapter" @@ -13,6 +14,9 @@ import {internalDev} from "../../src/services/environment" import {StaticProductInfo} from "../../src/components/claim/claimForm" import {MemoryRouter} from "react-router-dom" +jest.mock("uuid") +;(v4 as jest.Mock).mockImplementation(() => "test-uuid") + const baseUrl = "baseUrl/" const prescriptionId = "7A9089-A83008-56A03J" const context: AppContextValue = {baseUrl, environment: internalDev} diff --git a/packages/tool/site/client/tests/pages/dispensePage.spec.tsx b/packages/tool/site/client/tests/pages/dispensePage.spec.tsx index 473978748f..f1d380d009 100644 --- a/packages/tool/site/client/tests/pages/dispensePage.spec.tsx +++ b/packages/tool/site/client/tests/pages/dispensePage.spec.tsx @@ -1,5 +1,6 @@ import {waitFor} from "@testing-library/react" import {screen} from "@testing-library/dom" +import {v4} from "uuid" import pretty from "pretty" import * as React from "react" import MockAdapter from "axios-mock-adapter" @@ -12,6 +13,9 @@ import {axiosInstance} from "../../src/requests/axiosInstance" import {internalDev} from "../../src/services/environment" import {MemoryRouter} from "react-router-dom" +jest.mock("uuid") +;(v4 as jest.Mock).mockImplementation(() => "test-uuid") + const baseUrl = "baseUrl/" const prescriptionId = "7A9089-A83008-56A03J" const context: AppContextValue = {baseUrl, environment: internalDev} diff --git a/packages/tool/site/client/tests/pages/prescriptionSearchPage.spec.tsx b/packages/tool/site/client/tests/pages/prescriptionSearchPage.spec.tsx index 6e1e228826..627d4fbcd3 100644 --- a/packages/tool/site/client/tests/pages/prescriptionSearchPage.spec.tsx +++ b/packages/tool/site/client/tests/pages/prescriptionSearchPage.spec.tsx @@ -1,6 +1,7 @@ import {waitFor} from "@testing-library/react" import {screen} from "@testing-library/dom" import pretty from "pretty" +import {v4} from "uuid" import * as React from "react" import MockAdapter from "axios-mock-adapter" import userEvent from "@testing-library/user-event" @@ -16,6 +17,9 @@ import {DateRangeType} from "../../src/components/prescription-tracker/dateRange import {internalDev} from "../../src/services/environment" import {MemoryRouter} from "react-router-dom" +jest.mock("uuid") +;(v4 as jest.Mock).mockImplementation(() => "test-uuid") + const baseUrl = "baseUrl/" const prescriptionId = "003D4D-A99968-4C5AAJ" const nhsNumber = "9449304106" diff --git a/packages/tool/site/client/tests/pages/signPage.spec.tsx b/packages/tool/site/client/tests/pages/signPage.spec.tsx index 116d303319..1d2557bf6a 100644 --- a/packages/tool/site/client/tests/pages/signPage.spec.tsx +++ b/packages/tool/site/client/tests/pages/signPage.spec.tsx @@ -1,5 +1,6 @@ import {waitFor} from "@testing-library/react" import {screen} from "@testing-library/dom" +import {v4} from "uuid" import pretty from "pretty" import * as React from "react" import MockAdapter from "axios-mock-adapter" @@ -15,6 +16,9 @@ import {MomentInput} from "moment" import {internalDev} from "../../src/services/environment" import {MemoryRouter} from "react-router-dom" +jest.mock("uuid") +;(v4 as jest.Mock).mockImplementation(() => "test-uuid") + const baseUrl = "baseUrl/" const context: AppContextValue = {baseUrl, environment: internalDev} diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 297a1b0805..8624abb4f6 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -11,6 +11,7 @@ import {isLocal} from "../environment" import {URLSearchParams} from "url" import axios, { AxiosError, + AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, RawAxiosRequestHeaders @@ -23,8 +24,6 @@ import {DosageTranslationArray} from "../../routes/dose-to-text" type QueryParams = Record> -const axiosInstance = axios.create() - const getUrlSearchParams = (query: QueryParams): URLSearchParams => { const urlSearchParams = new URLSearchParams() Object.keys(query).forEach(key => { @@ -46,12 +45,14 @@ interface EpsResponse { class EpsClient { private request: Hapi.Request + private axiosInstance: AxiosInstance constructor(request: Hapi.Request) { this.request = request + this.axiosInstance = axios.create() const logger = request.logger - axiosInstance.interceptors.request.use((request: InternalAxiosRequestConfig) => { + this.axiosInstance.interceptors.request.use((request: InternalAxiosRequestConfig) => { logger.info({ apiCall: { request: { @@ -65,7 +66,7 @@ class EpsClient { return request }) - axiosInstance.interceptors.response.use((response: AxiosResponse) => { + this.axiosInstance.interceptors.response.use((response: AxiosResponse) => { logger.info({ apiCall: { response: { @@ -125,7 +126,7 @@ class EpsClient { async makePingRequest(): Promise { const basePath = this.getBasePath("prescribe") const url = `${CONFIG.apigeeEgressHost}/${basePath}/_ping` - return (await axiosInstance.get(url)).data + return (await this.axiosInstance.get(url)).data } async makeValidateRequest(body: FhirResource): Promise> { @@ -184,7 +185,7 @@ class EpsClient { Object.assign(headers, additionalHeaders) } - return axiosInstance.request({ + return this.axiosInstance.request({ url, method: body ? "POST" : "GET", headers, From d52304fe0cfd49305937fea306334fa21d32642e Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:14:10 +0000 Subject: [PATCH 07/22] use x-correlation-id --- .../site/client/src/requests/axiosInstance.ts | 3 +- .../__snapshots__/claimPage.spec.tsx.snap | 12 +- .../__snapshots__/dispensePage.spec.tsx.snap | 8 +- .../prescriptionSearchPage.spec.tsx.snap | 8 +- .../__snapshots__/signPage.spec.tsx.snap | 8 +- .../site/server/src/routes/api/convert.ts | 4 +- .../tool/site/server/src/routes/api/send.ts | 8 +- .../site/server/src/routes/dispense/claim.ts | 6 +- .../server/src/routes/dispense/dispense.ts | 8 +- .../server/src/routes/dispense/release.ts | 6 +- .../site/server/src/routes/dispense/return.ts | 6 +- .../server/src/routes/dispense/withdraw.ts | 6 +- .../site/server/src/routes/dose-to-text.ts | 4 +- .../server/src/routes/prescribe/cancel.ts | 6 +- .../src/routes/sign/uploadSignatures.ts | 5 +- .../site/server/src/routes/tracker/tracker.ts | 4 +- packages/tool/site/server/src/routes/util.ts | 6 + .../server/src/routes/validate/validator.ts | 4 +- .../src/services/communication/eps-client.ts | 188 +++++++++++++----- .../tool/site/server/src/services/session.ts | 6 +- 20 files changed, 210 insertions(+), 96 deletions(-) diff --git a/packages/tool/site/client/src/requests/axiosInstance.ts b/packages/tool/site/client/src/requests/axiosInstance.ts index 1466311c4c..ef43ffdc63 100644 --- a/packages/tool/site/client/src/requests/axiosInstance.ts +++ b/packages/tool/site/client/src/requests/axiosInstance.ts @@ -8,8 +8,7 @@ const axiosInstance = axios.create({ axiosInstance.interceptors.request.use( (config: InternalAxiosRequestConfig) => { const requestId = uuidv4() - console.log(`making epsat request id ${requestId}`) - config.headers["x-epsat-request-id"] = requestId + config.headers["x-correlation-id"] = requestId return config }, diff --git a/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap index 10235ee6d8..29c448d570 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/claimPage.spec.tsx.snap @@ -31,9 +31,9 @@ exports[`Displays an error if prescription-order not found 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
@@ -81,9 +81,9 @@ exports[`Displays an error if previous claim not found for amend 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
@@ -131,9 +131,9 @@ exports[`Displays an error on invalid response 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap index 981f4ca3ec..ff7637e003 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/dispensePage.spec.tsx.snap @@ -22,9 +22,9 @@ exports[`Displays an error if prescription-order not found 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
@@ -72,9 +72,9 @@ exports[`Displays an error on invalid response 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap index 3fbeb7c6c5..023b4ed589 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/prescriptionSearchPage.spec.tsx.snap @@ -160,9 +160,9 @@ exports[`Displays an error message if summary search returns an error 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
@@ -234,9 +234,9 @@ exports[`Displays an error message if summary search returns invalid response 1`
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap index a6513099c5..da10414eb6 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/signPage.spec.tsx.snap @@ -22,11 +22,11 @@ exports[`Displays error message if prepare errors present 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
   "Content-Type": "application/x-www-form-urlencoded",
   "nhsd-identity-authentication-method": "N3_SMARTCARD",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
@@ -93,11 +93,11 @@ exports[`Displays error message if redirect URI not present 1`] = `
Request Headers
-
{
+        
{
   "Accept": "application/json, text/plain, */*",
   "Content-Type": "application/x-www-form-urlencoded",
   "nhsd-identity-authentication-method": "N3_SMARTCARD",
-  "x-epsat-request-id": "test-uuid"
+  "x-correlation-id": "test-uuid"
 }
diff --git a/packages/tool/site/server/src/routes/api/convert.ts b/packages/tool/site/server/src/routes/api/convert.ts index 107be67b03..4c7dc4f7aa 100644 --- a/packages/tool/site/server/src/routes/api/convert.ts +++ b/packages/tool/site/server/src/routes/api/convert.ts @@ -2,6 +2,7 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" import * as fhir from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" import {getApigeeAccessTokenFromSession} from "../../services/session" +import {getCorrelationId} from "../util" export default [ { @@ -14,7 +15,8 @@ export default [ const epsClient = getEpsClient(accessToken, request) request.logger.debug(`Received Resource with id: ${resource.id}. Sending to Convert.`) - const convertedResource = await epsClient.makeConvertRequest(resource) + const correlationId = getCorrelationId(request) + const convertedResource = await epsClient.makeConvertRequest(resource, correlationId) request.logger.debug(`Converted ${resource.id}`) return responseToolkit.response(convertedResource).code(200) diff --git a/packages/tool/site/server/src/routes/api/send.ts b/packages/tool/site/server/src/routes/api/send.ts index e94f95ff44..9f890a61dc 100644 --- a/packages/tool/site/server/src/routes/api/send.ts +++ b/packages/tool/site/server/src/routes/api/send.ts @@ -9,6 +9,7 @@ import {getEpsClient} from "../../services/communication/eps-client" import * as fhir from "fhir/r4" import {SignatureDownloadResponse} from "../../services/communication/signing-client" import {getMedicationRequests} from "../../common/getResources" +import {getCorrelationId} from "../util" interface SuccessfulPrepareData { prescriptionId: string @@ -63,6 +64,7 @@ export default [ const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) + const correlationId = getCorrelationId(request) const results = [] for (const prepare of prepares) { @@ -88,10 +90,10 @@ export default [ // eslint-disable-next-line @typescript-eslint/no-explicit-any let sendResponse: any if (prepares.length === 1) { - sendResponse = await epsClient.makeSendRequest(sendRequest) - sendRequestHl7 = await epsClient.makeConvertRequest(sendRequest) + sendResponse = await epsClient.makeSendRequest(sendRequest, correlationId) + sendRequestHl7 = await epsClient.makeConvertRequest(sendRequest, correlationId) } else { - sendResponse = await epsClient.makeSendFhirRequest(sendRequest) + sendResponse = await epsClient.makeSendFhirRequest(sendRequest, correlationId) } results.push({ diff --git a/packages/tool/site/server/src/routes/dispense/claim.ts b/packages/tool/site/server/src/routes/dispense/claim.ts index e42bb6056e..4ff66d3015 100644 --- a/packages/tool/site/server/src/routes/dispense/claim.ts +++ b/packages/tool/site/server/src/routes/dispense/claim.ts @@ -8,6 +8,7 @@ import { } from "../../services/session" import {Claim} from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" +import {getCorrelationId} from "../util" export default [ { @@ -19,8 +20,9 @@ export default [ const claimRequest = payload.claim const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const claimResponse = await epsClient.makeClaimRequest(claimRequest) - const claimResponseHl7 = await epsClient.makeConvertRequest(claimRequest) + const correlationId = getCorrelationId(request) + const claimResponse = await epsClient.makeClaimRequest(claimRequest, correlationId) + const claimResponseHl7 = await epsClient.makeConvertRequest(claimRequest, correlationId) const success = claimResponse.statusCode === 200 if (success) { diff --git a/packages/tool/site/server/src/routes/dispense/dispense.ts b/packages/tool/site/server/src/routes/dispense/dispense.ts index cbd05bc3c4..665f6e8fc5 100644 --- a/packages/tool/site/server/src/routes/dispense/dispense.ts +++ b/packages/tool/site/server/src/routes/dispense/dispense.ts @@ -8,6 +8,7 @@ import { } from "../../services/session" import * as fhir from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" +import {getCorrelationId} from "../util" export interface MedicationDispense extends fhir.MedicationDispense { contained: Array @@ -27,8 +28,11 @@ export default [ const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const dispenseNotificationResponse = await epsClient.makeSendRequest(dispenseNotificationRequest) - const dispenseNotificationRequestHl7 = await epsClient.makeConvertRequest(dispenseNotificationRequest) + const correlationId = getCorrelationId(request) + const dispenseNotificationResponse = await epsClient.makeSendRequest(dispenseNotificationRequest, correlationId) + const dispenseNotificationRequestHl7 = await epsClient.makeConvertRequest( + dispenseNotificationRequest, correlationId + ) const success = dispenseNotificationResponse.statusCode === 200 if (success) { const medicationDispense = dispenseNotificationRequest?.entry diff --git a/packages/tool/site/server/src/routes/dispense/release.ts b/packages/tool/site/server/src/routes/dispense/release.ts index 2bf186e001..2e95a8ef1f 100644 --- a/packages/tool/site/server/src/routes/dispense/release.ts +++ b/packages/tool/site/server/src/routes/dispense/release.ts @@ -15,6 +15,7 @@ import { } from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" import {getMedicationRequests} from "../../common/getResources" +import {getCorrelationId} from "../util" interface DispenserDetails { odsCode: string @@ -30,8 +31,9 @@ export default [ const releaseRequest = request.payload as Parameters const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const releaseResponse = await epsClient.makeReleaseRequest(releaseRequest) - const releaseRequestHl7 = await epsClient.makeConvertRequest(releaseRequest) + const correlationId = getCorrelationId(request) + const releaseResponse = await epsClient.makeReleaseRequest(releaseRequest, correlationId) + const releaseRequestHl7 = await epsClient.makeConvertRequest(releaseRequest, correlationId) let withDispenser: DispenserDetails | undefined = undefined const releasedPrescriptionIds: Array = [] diff --git a/packages/tool/site/server/src/routes/dispense/return.ts b/packages/tool/site/server/src/routes/dispense/return.ts index c73fc50831..030a27845e 100644 --- a/packages/tool/site/server/src/routes/dispense/return.ts +++ b/packages/tool/site/server/src/routes/dispense/return.ts @@ -2,6 +2,7 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" import {getApigeeAccessTokenFromSession, removeFromSessionValue} from "../../services/session" import {Task} from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" +import {getCorrelationId} from "../util" export default [ { @@ -11,8 +12,9 @@ export default [ const releaseRequest = request.payload as Task const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const returnResponse = await epsClient.makeReturnRequest(releaseRequest) - const returnRequestHl7 = await epsClient.makeConvertRequest(releaseRequest) + const correlationId = getCorrelationId(request) + const returnResponse = await epsClient.makeReturnRequest(releaseRequest, correlationId) + const returnRequestHl7 = await epsClient.makeConvertRequest(releaseRequest, correlationId) const success = returnResponse.statusCode === 200 if (success) { removeFromSessionValue("released_prescription_ids", releaseRequest.groupIdentifier?.value, request) diff --git a/packages/tool/site/server/src/routes/dispense/withdraw.ts b/packages/tool/site/server/src/routes/dispense/withdraw.ts index 8050d1eaa0..40a1ea621f 100644 --- a/packages/tool/site/server/src/routes/dispense/withdraw.ts +++ b/packages/tool/site/server/src/routes/dispense/withdraw.ts @@ -9,6 +9,7 @@ import { } from "../../services/session" import {Bundle, Task} from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" +import {getCorrelationId} from "../util" export default [ { @@ -18,8 +19,9 @@ export default [ const withdrawRequest = request.payload as Task const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const withdrawResponse = await epsClient.makeWithdrawRequest(withdrawRequest) - const withdrawRequestHl7 = await epsClient.makeConvertRequest(withdrawRequest) + const correlationId = getCorrelationId(request) + const withdrawResponse = await epsClient.makeWithdrawRequest(withdrawRequest, correlationId) + const withdrawRequestHl7 = await epsClient.makeConvertRequest(withdrawRequest, correlationId) const success = withdrawResponse.statusCode === 200 if (success) { const prescriptionId = withdrawRequest.groupIdentifier?.value diff --git a/packages/tool/site/server/src/routes/dose-to-text.ts b/packages/tool/site/server/src/routes/dose-to-text.ts index 6d528ad713..89a106ddb9 100644 --- a/packages/tool/site/server/src/routes/dose-to-text.ts +++ b/packages/tool/site/server/src/routes/dose-to-text.ts @@ -2,6 +2,7 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" import {getApigeeAccessTokenFromSession} from "../services/session" import {getEpsClient} from "../services/communication/eps-client" import * as fhir from "fhir/r4" +import {getCorrelationId} from "./util" export interface DosageTranslation { identifier: Array @@ -16,7 +17,8 @@ export default [ const doseToTextRequest = request.payload as fhir.FhirResource const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const doseToTextResponse = await epsClient.makeDoseToTextRequest(doseToTextRequest) + const correlationId = getCorrelationId(request) + const doseToTextResponse = await epsClient.makeDoseToTextRequest(doseToTextRequest, correlationId) const epsResponse = doseToTextResponse.fhirResponse as DosageTranslationArray const doseToTextResults = epsResponse ?? [] const success = doseToTextResponse.statusCode === 200 diff --git a/packages/tool/site/server/src/routes/prescribe/cancel.ts b/packages/tool/site/server/src/routes/prescribe/cancel.ts index 23d1f66774..b89bb47a3b 100644 --- a/packages/tool/site/server/src/routes/prescribe/cancel.ts +++ b/packages/tool/site/server/src/routes/prescribe/cancel.ts @@ -2,6 +2,7 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" import {Bundle} from "fhir/r4" import {getEpsClient} from "../../services/communication/eps-client" import {getApigeeAccessTokenFromSession} from "../../services/session" +import {getCorrelationId} from "../util" export default [ { @@ -11,8 +12,9 @@ export default [ const cancelRequest = request.payload as Bundle const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const cancelResponse = await epsClient.makeSendRequest(cancelRequest) - const cancelResponseHl7 = await epsClient.makeConvertRequest(cancelRequest) + const correlationId = getCorrelationId(request) + const cancelResponse = await epsClient.makeSendRequest(cancelRequest, correlationId) + const cancelResponseHl7 = await epsClient.makeConvertRequest(cancelRequest, correlationId) const success = cancelResponse.statusCode === 200 return responseToolkit.response({ success: success, diff --git a/packages/tool/site/server/src/routes/sign/uploadSignatures.ts b/packages/tool/site/server/src/routes/sign/uploadSignatures.ts index d17701b704..593c3f3bda 100644 --- a/packages/tool/site/server/src/routes/sign/uploadSignatures.ts +++ b/packages/tool/site/server/src/routes/sign/uploadSignatures.ts @@ -3,7 +3,7 @@ import {getSigningClient} from "../../services/communication/signing-client" import {getEpsClient} from "../../services/communication/eps-client" import {getApigeeAccessTokenFromSession, getSessionValue, setSessionValue} from "../../services/session" import * as fhir from "fhir/r4" -import {getSessionPrescriptionIdsArray} from "../util" +import {getCorrelationId, getSessionPrescriptionIdsArray} from "../util" export default [ { @@ -16,9 +16,10 @@ export default [ const prescriptionIds = getSessionPrescriptionIdsArray(request) const successfulPreparePrescriptionIds = [] const signInHeaders = request.headers["nhsd-identity-authentication-method"] + const correlationId = getCorrelationId(request) for (const id of prescriptionIds) { const prepareRequest = getSessionValue(`prepare_request_${id}`, request) - const prepareResponse = await epsClient.makePrepareRequest(prepareRequest) + const prepareResponse = await epsClient.makePrepareRequest(prepareRequest, correlationId) setSessionValue(`prepare_response_${id}`, prepareResponse, request) if (!prepareResponseIsError(prepareResponse)) { successfulPreparePrescriptionIds.push(id) diff --git a/packages/tool/site/server/src/routes/tracker/tracker.ts b/packages/tool/site/server/src/routes/tracker/tracker.ts index 50235168fc..64b9eef67a 100644 --- a/packages/tool/site/server/src/routes/tracker/tracker.ts +++ b/packages/tool/site/server/src/routes/tracker/tracker.ts @@ -1,6 +1,7 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" import {getEpsClient} from "../../services/communication/eps-client" import {getApigeeAccessTokenFromSession} from "../../services/session" +import {getCorrelationId} from "../util" export default [ { @@ -9,7 +10,8 @@ export default [ handler: async (request: Hapi.Request, h: Hapi.ResponseToolkit): Promise => { const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const response = await epsClient.makeGetTaskTrackerRequest(request.query) + const correlationId = getCorrelationId(request) + const response = await epsClient.makeGetTaskTrackerRequest(request.query, correlationId) return h.response(response).code(200) } } diff --git a/packages/tool/site/server/src/routes/util.ts b/packages/tool/site/server/src/routes/util.ts index b3468f9098..5d77a391a6 100644 --- a/packages/tool/site/server/src/routes/util.ts +++ b/packages/tool/site/server/src/routes/util.ts @@ -1,5 +1,6 @@ import Hapi from "@hapi/hapi" import {getSessionValue} from "../services/session" +import * as uuid from "uuid" export type PrescriptionId = { bundleId: string | undefined @@ -13,3 +14,8 @@ export function getUtcEpochSeconds(): number { export function getSessionPrescriptionIdsArray(request: Hapi.Request): Array { return getSessionValue("prescription_ids", request).map((id: PrescriptionId) => id.prescriptionId) } + +export function getCorrelationId(request: Hapi.Request): string { + const correlationId = request.headers["x-correlation-id"] + return correlationId || uuid.v4() +} diff --git a/packages/tool/site/server/src/routes/validate/validator.ts b/packages/tool/site/server/src/routes/validate/validator.ts index a74b7d7193..4d930958d9 100644 --- a/packages/tool/site/server/src/routes/validate/validator.ts +++ b/packages/tool/site/server/src/routes/validate/validator.ts @@ -2,6 +2,7 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" import {getApigeeAccessTokenFromSession} from "../../services/session" import {getEpsClient} from "../../services/communication/eps-client" import {FhirResource} from "fhir/r4" +import {getCorrelationId} from "../util" export default [ { @@ -11,7 +12,8 @@ export default [ const validateRequest = request.payload as FhirResource const accessToken = getApigeeAccessTokenFromSession(request) const epsClient = getEpsClient(accessToken, request) - const validateResponse = await epsClient.makeValidateRequest(validateRequest) + const correlationId = getCorrelationId(request) + const validateResponse = await epsClient.makeValidateRequest(validateRequest, correlationId) const sendResult = { success: !validateResponse.fhirResponse.issue.some(issue => issue.severity === "error"), request: validateRequest, diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 8624abb4f6..41e64e86a9 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -43,6 +43,25 @@ interface EpsResponse { spineResponse?: string } +interface ApiCall { + path: string, + api: string, + body?: unknown, + params?: URLSearchParams, + requestId?: string, + correlationId? : string, + additionalHeaders?: RawAxiosRequestHeaders +} + +interface GetEpsResponse { + endpoint: string, + api: string, + body?: FhirResource, + params?: URLSearchParams, + fhirResponseOnly?: boolean, + correlationId: string +} + class EpsClient { private request: Hapi.Request private axiosInstance: AxiosInstance @@ -90,37 +109,79 @@ class EpsClient { } - async makeGetTaskTrackerRequest(query: QueryParams): Promise { + async makeGetTaskTrackerRequest(query: QueryParams, correlationId: string): Promise { const urlSearchParams = getUrlSearchParams(query) - return (await this.makeApiCall("Task", "prescribe", undefined, urlSearchParams)).data + return (await this.makeApiCall({ + path: "Task", + api: "prescribe", + params: urlSearchParams, + correlationId + })).data } - async makePrepareRequest(body: Bundle): Promise { - return (await this.makeApiCall("$prepare", "prescribe", body)).data + async makePrepareRequest(body: Bundle, correlationId: string): Promise { + return (await this.makeApiCall({ + path: "$prepare", + api: "prescribe", + body, + correlationId + })).data } - async makeSendRequest(body: Bundle): Promise> { - return await this.getEpsResponse("$process-message", "prescribe", body) + async makeSendRequest(body: Bundle, correlationId: string): Promise> { + return await this.getEpsResponse({ + endpoint: "$process-message", + api: "prescribe", + body, + correlationId + }) } - async makeSendFhirRequest(body: Bundle): Promise> { - return await this.getEpsResponse("$process-message", "prescribe", body, undefined, true) + async makeSendFhirRequest(body: Bundle, correlationId: string): Promise> { + return await this.getEpsResponse({ + endpoint: "$process-message", + api: "prescribe", + body, + correlationId, + fhirResponseOnly: true + }) } - async makeReleaseRequest(body: Parameters): Promise> { - return await this.getEpsResponse("Task/$release", "dispense", body) + async makeReleaseRequest( + body: Parameters, correlationId: string): Promise> { + return await this.getEpsResponse({ + endpoint: "Task/$release", + api: "dispense", + body, + correlationId + }) } - async makeReturnRequest(body: Task): Promise> { - return await this.getEpsResponse("Task", "dispense", body) + async makeReturnRequest(body: Task, correlationId: string): Promise> { + return await this.getEpsResponse({ + endpoint: "Task", + api: "dispense", + body, + correlationId + }) } - async makeWithdrawRequest(body: Task): Promise> { - return await this.getEpsResponse("Task", "dispense", body) + async makeWithdrawRequest(body: Task, correlationId: string): Promise> { + return await this.getEpsResponse({ + endpoint: "Task", + api: "dispense", + body, + correlationId + }) } - async makeClaimRequest(body: Claim): Promise> { - return await this.getEpsResponse("Claim", "dispense", body) + async makeClaimRequest(body: Claim, correlationId: string): Promise> { + return await this.getEpsResponse({ + endpoint: "Claim", + api: "dispense", + body: body, + correlationId + }) } async makePingRequest(): Promise { @@ -129,68 +190,88 @@ class EpsClient { return (await this.axiosInstance.get(url)).data } - async makeValidateRequest(body: FhirResource): Promise> { - const requestId = uuid.v4() - // eslint-disable-next-line max-len - const response = await this.makeApiCall("$validate", "prescribe", body, undefined, requestId, {"x-show-validation-warnings": "true"}) + async makeValidateRequest(body: FhirResource, correlationId: string): Promise> { + const response = await this.makeApiCall({ + path: "$validate", + api: "prescribe", + body, + correlationId, + additionalHeaders: {"x-show-validation-warnings": "true"} + }) const statusCode = response.status const fhirResponse = response.data return {statusCode, fhirResponse} } - async makeConvertRequest(body: FhirResource): Promise { - const response = (await this.makeApiCall("$convert", "prescribe", body)).data + async makeConvertRequest(body: FhirResource, correlationId: string): Promise { + const response = (await this.makeApiCall({ + path: "$convert", + api: "prescribe", + body, + correlationId + })).data return typeof response === "string" ? response : JSON.stringify(response, null, 2) } - async makeDoseToTextRequest(body: FhirResource): Promise> { + async makeDoseToTextRequest(body: FhirResource, correlationId: string): Promise> { const requestId = uuid.v4() - const response = await this.makeApiCall( - "$dose-to-text", "prescribe", body, undefined, requestId) + const response = await this.makeApiCall({ + path: "$dose-to-text", + api: "prescribe", + body, + requestId, + correlationId + }) const statusCode = response.status const doseToTextResponse = response.data return {statusCode, fhirResponse: doseToTextResponse} } private async getEpsResponse( - endpoint: string, - api: string, - body?: FhirResource, - params?: URLSearchParams, - fhirResponseOnly?: boolean + params: GetEpsResponse ) { const requestId = uuid.v4() - const response = await this.makeApiCall(endpoint, api, body, params, requestId) + const response = await this.makeApiCall({ + path: params.endpoint, + api: params.api, + body: params.body, + params: params.params, + requestId, + correlationId: params.correlationId + }) const statusCode = response.status const fhirResponse = response.data - const spineResponse = fhirResponseOnly + const spineResponse = params.fhirResponseOnly ? "" - // eslint-disable-next-line max-len - : (await this.makeApiCall(endpoint, api, body, params, requestId, {"x-raw-response": "true"})).data + + : (await this.makeApiCall({ + path: params.endpoint, + api: params.api, + body: params.body, + params: params.params, + requestId, + correlationId: params.correlationId, + additionalHeaders: {"x-raw-response": "true"} + })).data return {statusCode, fhirResponse, spineResponse: this.asString(spineResponse)} } private async makeApiCall( - path: string, - api: string, - body?: unknown, - params?: URLSearchParams, - requestId?: string, - additionalHeaders?: RawAxiosRequestHeaders + apiCall: ApiCall ): Promise> { - const basePath = this.getBasePath(api) - const url = `${CONFIG.apigeeEgressHost}/${basePath}/FHIR/R4/${path}` - const headers: RawAxiosRequestHeaders = this.getHeaders(requestId) - if (additionalHeaders) { - Object.assign(headers, additionalHeaders) + const basePath = this.getBasePath(apiCall.api) + const url = `${CONFIG.apigeeEgressHost}/${basePath}/FHIR/R4/${apiCall.path}` + const headers: RawAxiosRequestHeaders = this.getHeaders(apiCall.requestId, apiCall.correlationId) + if (apiCall.additionalHeaders) { + Object.assign(headers, apiCall.additionalHeaders) } return this.axiosInstance.request({ url, - method: body ? "POST" : "GET", + method: apiCall.body ? "POST" : "GET", headers, - data: body, - params + data: apiCall.body, + params: apiCall.params }) } @@ -206,10 +287,10 @@ class EpsClient { : `${CONFIG.basePath}`.replace("eps-api-tool", replacementString) } - protected getHeaders(requestId: string | undefined): RawAxiosRequestHeaders { + protected getHeaders(requestId: string | undefined, correlationId: string | undefined): RawAxiosRequestHeaders { return { "x-request-id": requestId ?? uuid.v4(), - "x-correlation-id": uuid.v4() + "x-correlation-id": correlationId ?? uuid.v4() } } @@ -243,11 +324,14 @@ class LiveEpsClient extends EpsClient { this.accessToken = accessToken } - protected override getHeaders(requestId: string | undefined): RawAxiosRequestHeaders { + protected override getHeaders( + requestId: string | undefined, + correlationId: string | undefined + ): RawAxiosRequestHeaders { return { "Authorization": `Bearer ${this.accessToken}`, "x-request-id": requestId ?? uuid.v4(), - "x-correlation-id": uuid.v4() + "x-correlation-id": correlationId ?? uuid.v4() } } } diff --git a/packages/tool/site/server/src/services/session.ts b/packages/tool/site/server/src/services/session.ts index 09f8fbd090..f611fe7a53 100644 --- a/packages/tool/site/server/src/services/session.ts +++ b/packages/tool/site/server/src/services/session.ts @@ -9,9 +9,9 @@ export function getSessionValue(key: string, request: Hapi.Request): any { const sessionValue = request.yar.get(key) if (isLocal(CONFIG.environment)) { if (sessionValue === null) { - console.error(`Failed to retrieve session value for key: ${key}`) + request.logger.error(`Failed to retrieve session value for key: ${key}`) } else { - console.log(`Retrieved ${key} from session with value: ${JSON.stringify(sessionValue)}`) + request.logger.info(`Retrieved ${key} from session with value: ${JSON.stringify(sessionValue)}`) } } if (sessionValue && Object.keys(sessionValue).length === 1 && Object.keys(sessionValue)[0] === "arrayValues") { @@ -30,7 +30,7 @@ export function setSessionValue(key: string, value: unknown, request: Hapi.Reque value = {arrayValues: value} } if (isLocal(CONFIG.environment)) { - console.log(`Saving ${key} to session with value: ${JSON.stringify(value)}`) + request.logger.info(`Saving ${key} to session with value: ${JSON.stringify(value)}`) } request.yar.set(key, value) } From d130377a96aeecf0c208e5b7518b1830dee96ffb Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:15:50 +0000 Subject: [PATCH 08/22] remove more console.logs --- packages/tool/site/server/src/routes/auth/callback.ts | 2 +- packages/tool/site/server/src/routes/auth/login.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/tool/site/server/src/routes/auth/callback.ts b/packages/tool/site/server/src/routes/auth/callback.ts index 397e6cfe6a..54b2460aee 100644 --- a/packages/tool/site/server/src/routes/auth/callback.ts +++ b/packages/tool/site/server/src/routes/auth/callback.ts @@ -49,7 +49,7 @@ export default { return h.redirect(CONFIG.baseUrl) } catch (e) { - console.log(`Callback failed: ${e}`) + request.logger.error(`Callback failed: ${e}`) return h.response({error: e}) } } diff --git a/packages/tool/site/server/src/routes/auth/login.ts b/packages/tool/site/server/src/routes/auth/login.ts index 38ee8bac54..9a89a80433 100644 --- a/packages/tool/site/server/src/routes/auth/login.ts +++ b/packages/tool/site/server/src/routes/auth/login.ts @@ -101,7 +101,7 @@ export default { const scopes = ["openid", "profile"] const redirectUri = getRedirectUri(authorizationUri, CONFIG.cis2AppClientId, callbackUri, scopes) - console.log(`Redirecting browser to: ${redirectUri}`) + request.logger.info(`Redirecting browser to: ${redirectUri}`) return h.response({redirectUri}) } @@ -110,7 +110,7 @@ export default { const authorizationUri = `${CONFIG.publicApigeeHost}/oauth2/authorize` const redirectUri = getRedirectUri(authorizationUri, CONFIG.apigeeAppClientId, callbackUri) - console.log(`Redirecting browser to: ${redirectUri}`) + request.logger.info(`Redirecting browser to: ${redirectUri}`) return h.response({redirectUri}) } @@ -118,7 +118,7 @@ export default { const authorizationUri = `${CONFIG.publicApigeeHost}/oauth2-mock/authorize` const redirectUri = getRedirectUri(authorizationUri, CONFIG.apigeeAppClientId, callbackUri) - console.log(`Redirecting browser to: ${redirectUri}`) + request.logger.info(`Redirecting browser to: ${redirectUri}`) return h.response({redirectUri}) } } From 3c036ad2181993e153e2f5a3b79a4df4eaa69bb4 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:52:44 +0000 Subject: [PATCH 09/22] remove convert endpoint from published spec --- .github/scripts/deploy_api.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/scripts/deploy_api.sh b/.github/scripts/deploy_api.sh index 1cca7785ed..00012704b9 100755 --- a/.github/scripts/deploy_api.sh +++ b/.github/scripts/deploy_api.sh @@ -146,6 +146,7 @@ echo "Removing dummy paths before publishing spec" jq 'del(."paths"."/FHIR/R4/$process-message")' "$SPEC_PATH" > temp.json && mv temp.json "${SPEC_PATH}" jq 'del(."paths"."/FHIR/R4//FHIR/R4/Task")' "$SPEC_PATH" > temp.json && mv temp.json "${SPEC_PATH}" jq 'del(."paths"."/FHIR/R4/$validate")' "$SPEC_PATH" > temp.json && mv temp.json "${SPEC_PATH}" +jq 'del(."paths"."/FHIR/R4/$convert")' "$SPEC_PATH" > temp.json && mv temp.json "${SPEC_PATH}" if [[ "${APIGEE_ENVIRONMENT}" == "int" ]]; then echo From cdf809bdcbd16ac3c8ef1bf47c2866cd71ae2d2c Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:45:49 +0000 Subject: [PATCH 10/22] show epsat config page in all environments --- .gitallowed | 5 +++ .../site/client/src/components/pageHeader.tsx | 2 +- .../tool/site/client/src/pages/configPage.tsx | 43 +++++++++++-------- packages/tool/site/server/src/app.ts | 11 +---- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/.gitallowed b/.gitallowed index a703f62f6b..9d39d47e5c 100644 --- a/.gitallowed +++ b/.gitallowed @@ -47,6 +47,9 @@ super\(\"1\.2 ^[a-f0-9]{40}:?packages\/tool\/site\/client\/tests\/pages\/__snapshots__\/.*snap ^packages\/tool\/site\/client\/tests\/pages\/__snapshots__\/.*snap +# CONFIG.sessionKey is fine +CONFIG.sessionKey + # files that no longer exist or have been moved ^[a-f0-9]{40}:.*sds-api\/spine-directory-service\/sds\/lookup\/tests\/data\/my_real_server_schema\.json ^[a-f0-9]{40}:.*sds-api\/spine-directory-service\/sds\/request\/tests\/examples\/single_device\.json @@ -339,6 +342,8 @@ super\(\"1\.2 ^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/app\.ts:65 ^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/app\.ts:87 ^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/app\.ts:227 +^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/app\.ts:54 +^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/app\.ts:76 ^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/routes\/api\/send\.ts:150 ^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/routes\/auth\/refresh\.ts:12 ^[a-f0-9]{40}:packages\/tool\/site\/server\/src\/routes\/sign\/downloadSignatures\.ts:29 diff --git a/packages/tool/site/client/src/components/pageHeader.tsx b/packages/tool/site/client/src/components/pageHeader.tsx index fc643dedad..f9dbbf63aa 100644 --- a/packages/tool/site/client/src/components/pageHeader.tsx +++ b/packages/tool/site/client/src/components/pageHeader.tsx @@ -22,7 +22,7 @@ export const PageHeader: React.FC = ({loggedIn}) => {
- {loggedIn && (isInternalDev(environment) || isInternalDevSandbox(environment) || isQa(environment)) ? ( + {loggedIn ? (
EPSAT - Electronic Prescription Service API Tool diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index 89c386e857..8d7f50291a 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -7,6 +7,7 @@ import {Field, Formik} from "formik" import {axiosInstance} from "../requests/axiosInstance" import BackButton from "../components/common/backButton" import SuccessOrFail from "../components/common/successOrFail" +import { isInternalDev, isInternalDevSandbox, isQa } from "../services/environment" interface ConfigFormValues { useSigningMock: boolean @@ -20,9 +21,10 @@ interface ConfigResponse { } const ConfigPage: React.FC = () => { - const {baseUrl} = useContext(AppContext) + const {baseUrl, environment} = useContext(AppContext) const [configUpdateSuccess, setConfigUpdateSuccess] = useState(undefined) const initialValues = {useSigningMock: false, epsPrNumber: "", signingPrNumber: "", useProxygen: false} + const isDevOrQa = isInternalDev(environment) || isInternalDevSandbox(environment) || isQa(environment) if (configUpdateSuccess !== undefined) { return <> @@ -44,26 +46,33 @@ const ConfigPage: React.FC = () => {
- - - - - Use Signing Mock - - + {isDevOrQa && + + } - Use Proxygen + Use Proxygen deployed APIs - {!formik.values.useSigningMock && !formik.values.useProxygen && + {isDevOrQa && + + } + + {isDevOrQa && + + + Use Signing Mock + + + } + {!formik.values.useSigningMock && isDevOrQa && { width={30} label="Signing PR Number" /> - } + }
diff --git a/packages/tool/site/server/src/app.ts b/packages/tool/site/server/src/app.ts index 341227232d..de2fdc808e 100644 --- a/packages/tool/site/server/src/app.ts +++ b/packages/tool/site/server/src/app.ts @@ -5,12 +5,7 @@ import Vision from "@hapi/vision" import * as inert from "@hapi/inert" import Yar from "@hapi/yar" import Cookie from "@hapi/cookie" -import { - isDev, - isLocal, - isQa, - isSandbox -} from "./services/environment" +import {isLocal, isSandbox} from "./services/environment" import axios from "axios" import {CONFIG} from "./config" import {getSessionValue} from "./services/session" @@ -198,9 +193,7 @@ function addViewRoutes(server: Hapi.Server) { server.route(addView("dose-to-text")) } - if (isDev(CONFIG.environment) || isLocal(CONFIG.environment) || isQa(CONFIG.environment)) { - server.route(addView("config")) - } + server.route(addView("config")) function addHomeView(): Hapi.ServerRoute { return addView("/") From 0ee7ffd7e21710ff679fa7a7ad5e8ab462051dcc Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:47:54 +0000 Subject: [PATCH 11/22] remove unused spaces --- packages/tool/site/client/src/pages/configPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index 8d7f50291a..f582afaec7 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -80,7 +80,7 @@ const ConfigPage: React.FC = () => { width={30} label="Signing PR Number" /> - } + } From 2df17e562bf13907981c0cdb2277bb7977a351bf Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:49:13 +0000 Subject: [PATCH 12/22] no need for a var --- packages/tool/site/client/src/requests/axiosInstance.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/tool/site/client/src/requests/axiosInstance.ts b/packages/tool/site/client/src/requests/axiosInstance.ts index ef43ffdc63..eae2de3919 100644 --- a/packages/tool/site/client/src/requests/axiosInstance.ts +++ b/packages/tool/site/client/src/requests/axiosInstance.ts @@ -7,9 +7,7 @@ const axiosInstance = axios.create({ axiosInstance.interceptors.request.use( (config: InternalAxiosRequestConfig) => { - const requestId = uuidv4() - config.headers["x-correlation-id"] = requestId - + config.headers["x-correlation-id"] = uuidv4() return config }, (error) => { From d2cb4428ef6f69967bb621a8de29e3266a9a9949 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:57:53 +0000 Subject: [PATCH 13/22] update snapshots --- .../pages/__snapshots__/configPage.spec.tsx.snap | 12 ++++++------ .../pages/__snapshots__/withdrawPage.spec.tsx.snap | 2 +- .../server/src/services/communication/eps-client.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap index d67c09fe47..f78f73be5b 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap @@ -5,15 +5,15 @@ exports[`Displays config form 1`] = `
-
+
-
-
+
+
-
+
-
-
+
+
diff --git a/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap index 77d7484abc..37693791ee 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap @@ -234,7 +234,7 @@ exports[`Withdraw Page When there are two dispense notifications should match th
-
+
diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 41e64e86a9..0daed40c15 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -49,7 +49,7 @@ interface ApiCall { body?: unknown, params?: URLSearchParams, requestId?: string, - correlationId? : string, + correlationId : string, additionalHeaders?: RawAxiosRequestHeaders } From 7a7e05ebcea8795c3326aa7816d101b603b9eabe Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 19:03:26 +0000 Subject: [PATCH 14/22] do not need environments to work for epsat deployment --- packages/tool/azure/azure-release-pipeline.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/tool/azure/azure-release-pipeline.yml b/packages/tool/azure/azure-release-pipeline.yml index 2b65ef4ab4..d963a92f36 100644 --- a/packages/tool/azure/azure-release-pipeline.yml +++ b/packages/tool/azure/azure-release-pipeline.yml @@ -41,15 +41,12 @@ extends: proxy_path: sandbox - environment: internal-qa depends_on: - - internal_dev - qa_manual_approval - environment: sandbox proxy_path: sandbox depends_on: - - internal_qa - ptl_manual_approval - environment: int proxy_path: live depends_on: - - internal_qa - ptl_manual_approval From e89c0a7e94c3cebc080c1a4020dd288e164f632a Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 19:52:14 +0000 Subject: [PATCH 15/22] update snapshots --- .../client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap index 37693791ee..77d7484abc 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/withdrawPage.spec.tsx.snap @@ -234,7 +234,7 @@ exports[`Withdraw Page When there are two dispense notifications should match th
-
+
From 912657d4e0fd393613d29f9b18242e1e6f000520 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:43:30 +0000 Subject: [PATCH 16/22] use an enum --- .../src/services/communication/eps-client.ts | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 0daed40c15..75d1f53721 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -43,9 +43,14 @@ interface EpsResponse { spineResponse?: string } -interface ApiCall { +enum ApiType { + Prescribing = "fhir-prescribing", + Dispensing = "fhir-dispensing" +} + +interface ApiCallParameters { path: string, - api: string, + apiType: ApiType, body?: unknown, params?: URLSearchParams, requestId?: string, @@ -53,9 +58,9 @@ interface ApiCall { additionalHeaders?: RawAxiosRequestHeaders } -interface GetEpsResponse { +interface GetEpsResponseParameters { endpoint: string, - api: string, + apiType: ApiType, body?: FhirResource, params?: URLSearchParams, fhirResponseOnly?: boolean, @@ -113,7 +118,7 @@ class EpsClient { const urlSearchParams = getUrlSearchParams(query) return (await this.makeApiCall({ path: "Task", - api: "prescribe", + apiType: ApiType.Prescribing, params: urlSearchParams, correlationId })).data @@ -122,7 +127,7 @@ class EpsClient { async makePrepareRequest(body: Bundle, correlationId: string): Promise { return (await this.makeApiCall({ path: "$prepare", - api: "prescribe", + apiType: ApiType.Prescribing, body, correlationId })).data @@ -131,7 +136,7 @@ class EpsClient { async makeSendRequest(body: Bundle, correlationId: string): Promise> { return await this.getEpsResponse({ endpoint: "$process-message", - api: "prescribe", + apiType: ApiType.Prescribing, body, correlationId }) @@ -140,7 +145,7 @@ class EpsClient { async makeSendFhirRequest(body: Bundle, correlationId: string): Promise> { return await this.getEpsResponse({ endpoint: "$process-message", - api: "prescribe", + apiType: ApiType.Prescribing, body, correlationId, fhirResponseOnly: true @@ -151,7 +156,7 @@ class EpsClient { body: Parameters, correlationId: string): Promise> { return await this.getEpsResponse({ endpoint: "Task/$release", - api: "dispense", + apiType: ApiType.Dispensing, body, correlationId }) @@ -160,7 +165,7 @@ class EpsClient { async makeReturnRequest(body: Task, correlationId: string): Promise> { return await this.getEpsResponse({ endpoint: "Task", - api: "dispense", + apiType: ApiType.Dispensing, body, correlationId }) @@ -169,7 +174,7 @@ class EpsClient { async makeWithdrawRequest(body: Task, correlationId: string): Promise> { return await this.getEpsResponse({ endpoint: "Task", - api: "dispense", + apiType: ApiType.Dispensing, body, correlationId }) @@ -178,14 +183,14 @@ class EpsClient { async makeClaimRequest(body: Claim, correlationId: string): Promise> { return await this.getEpsResponse({ endpoint: "Claim", - api: "dispense", + apiType: ApiType.Dispensing, body: body, correlationId }) } async makePingRequest(): Promise { - const basePath = this.getBasePath("prescribe") + const basePath = this.getBasePath(ApiType.Prescribing) const url = `${CONFIG.apigeeEgressHost}/${basePath}/_ping` return (await this.axiosInstance.get(url)).data } @@ -193,7 +198,7 @@ class EpsClient { async makeValidateRequest(body: FhirResource, correlationId: string): Promise> { const response = await this.makeApiCall({ path: "$validate", - api: "prescribe", + apiType: ApiType.Prescribing, body, correlationId, additionalHeaders: {"x-show-validation-warnings": "true"} @@ -206,7 +211,7 @@ class EpsClient { async makeConvertRequest(body: FhirResource, correlationId: string): Promise { const response = (await this.makeApiCall({ path: "$convert", - api: "prescribe", + apiType: ApiType.Prescribing, body, correlationId })).data @@ -217,7 +222,7 @@ class EpsClient { const requestId = uuid.v4() const response = await this.makeApiCall({ path: "$dose-to-text", - api: "prescribe", + apiType: ApiType.Prescribing, body, requestId, correlationId @@ -228,38 +233,38 @@ class EpsClient { } private async getEpsResponse( - params: GetEpsResponse + requestParameters: GetEpsResponseParameters ) { const requestId = uuid.v4() const response = await this.makeApiCall({ - path: params.endpoint, - api: params.api, - body: params.body, - params: params.params, + path: requestParameters.endpoint, + apiType: requestParameters.apiType, + body: requestParameters.body, + params: requestParameters.params, requestId, - correlationId: params.correlationId + correlationId: requestParameters.correlationId }) const statusCode = response.status const fhirResponse = response.data - const spineResponse = params.fhirResponseOnly + const spineResponse = requestParameters.fhirResponseOnly ? "" : (await this.makeApiCall({ - path: params.endpoint, - api: params.api, - body: params.body, - params: params.params, + path: requestParameters.endpoint, + apiType: requestParameters.apiType, + body: requestParameters.body, + params: requestParameters.params, requestId, - correlationId: params.correlationId, + correlationId: requestParameters.correlationId, additionalHeaders: {"x-raw-response": "true"} })).data return {statusCode, fhirResponse, spineResponse: this.asString(spineResponse)} } private async makeApiCall( - apiCall: ApiCall + apiCall: ApiCallParameters ): Promise> { - const basePath = this.getBasePath(apiCall.api) + const basePath = this.getBasePath(apiCall.apiType) const url = `${CONFIG.apigeeEgressHost}/${basePath}/FHIR/R4/${apiCall.path}` const headers: RawAxiosRequestHeaders = this.getHeaders(apiCall.requestId, apiCall.correlationId) if (apiCall.additionalHeaders) { @@ -275,13 +280,10 @@ class EpsClient { }) } - protected getBasePath(api: string): string { + protected getBasePath(apiType: ApiType): string { const prNumber = getSessionValue("eps_pr_number", this.request) const useProxygen = getSessionValue("use_proxygen", this.request) - let replacementString = "electronic-prescriptions" - if (useProxygen) { - replacementString = api==="prescribe" ? "fhir-prescribing" : "fhir-dispensing" - } + const replacementString = useProxygen ? apiType : "electronic-prescriptions" return prNumber ? `${replacementString}-pr-${prNumber}` : `${CONFIG.basePath}`.replace("eps-api-tool", replacementString) From db625f2939a5bfa57bba1c093191522b59df465f Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:02:19 +0000 Subject: [PATCH 17/22] fix tracker in proxygen epsat --- .../tool/site/server/src/services/communication/eps-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index 75d1f53721..d563b8120e 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -118,7 +118,7 @@ class EpsClient { const urlSearchParams = getUrlSearchParams(query) return (await this.makeApiCall({ path: "Task", - apiType: ApiType.Prescribing, + apiType: ApiType.Dispensing, params: urlSearchParams, correlationId })).data From 28ebaec2e83ddfeed607586c1185da58b0a0586a Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:59:28 +0000 Subject: [PATCH 18/22] set default config for dev --- packages/tool/e2e-tests/helpers.ts | 13 ------ .../prescribe/editPrescription.spec.ts | 2 - .../tool/site/client/src/pages/configPage.tsx | 33 ++++++++++++-- .../tool/site/client/src/pages/loginPage.tsx | 14 +++++- .../client/tests/pages/loginPage.spec.tsx | 6 ++- .../site/server/src/routes/config/config.ts | 43 +++++++++++++------ packages/tool/site/server/src/routes/index.ts | 2 +- 7 files changed, 80 insertions(+), 33 deletions(-) diff --git a/packages/tool/e2e-tests/helpers.ts b/packages/tool/e2e-tests/helpers.ts index 1643cdac29..5e24b28701 100644 --- a/packages/tool/e2e-tests/helpers.ts +++ b/packages/tool/e2e-tests/helpers.ts @@ -91,7 +91,6 @@ export async function getElement( export async function sendPrescriptionUserJourney(driver: ThenableWebDriver): Promise { await loginViaSimulatedAuthSmartcardUser(driver) - await setMockSigningConfig(driver) await createPrescription(driver) await loadPredefinedExamplePrescription(driver) await sendPrescription(driver) @@ -101,7 +100,6 @@ export async function sendPrescriptionUserJourney(driver: ThenableWebDriver): Pr export async function sendBulkPrescriptionUserJourney(driver: ThenableWebDriver, fileInfo: FileUploadInfo, successfulResultCountExpected: number): Promise { await loginViaSimulatedAuthSmartcardUser(driver) - await setMockSigningConfig(driver) await createPrescription(driver) await loadTestData(driver, fileInfo) await sendPrescription(driver) @@ -123,7 +121,6 @@ export async function prescriptionIntoCanceledState(driver: ThenableWebDriver, f export async function sendPrescriptionSingleMessageUserJourney(driver: ThenableWebDriver, fileUploadInfo: FileUploadInfo): Promise { await loginViaSimulatedAuthSmartcardUser(driver) - await setMockSigningConfig(driver) await createPrescription(driver) await loadTestData(driver, fileUploadInfo) await sendPrescription(driver) @@ -354,16 +351,6 @@ export async function sendPrescription(driver: ThenableWebDriver): Promise finaliseWebAction(driver, "SENDING PRESCRIPTION...") } -export async function setMockSigningConfig(driver: ThenableWebDriver): Promise { - await (await getElement(driver, configLink)).click() - await driver.wait(until.elementLocated(configPageTitle)) - await waitForPageToRender() - - await (await getElement(driver, By.name("useSigningMock"))).click() - await (await getElement(driver, configButton)).click() - await (await getElement(driver, backButton)).click() -} - export async function checkApiResult(driver: ThenableWebDriver, fhirOnly?: boolean): Promise { await driver.wait(until.elementsLocated(fhirRequestExpander), apiTimeout) if (!fhirOnly) { diff --git a/packages/tool/e2e-tests/prescribe/editPrescription.spec.ts b/packages/tool/e2e-tests/prescribe/editPrescription.spec.ts index 173c368991..6b42b140af 100644 --- a/packages/tool/e2e-tests/prescribe/editPrescription.spec.ts +++ b/packages/tool/e2e-tests/prescribe/editPrescription.spec.ts @@ -7,7 +7,6 @@ import { loadPredefinedExamplePrescription, loginViaSimulatedAuthSmartcardUser, sendPrescription, - setMockSigningConfig, tenTimesDefaultWaitTimeout, viewPrescriptionUserJourney, waitForPageToRender @@ -29,7 +28,6 @@ async function editPrescriptionOrganisationUserJourney( newOrganisation: string ): Promise { await loginViaSimulatedAuthSmartcardUser(driver) - await setMockSigningConfig(driver) await createPrescription(driver) await loadPredefinedExamplePrescription(driver) await editPrescriptionOrganisation(driver, newOrganisation) diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index f582afaec7..ca79c564a2 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import {useContext, useState} from "react" +import {useContext, useState, useEffect, useCallback} from "react" import {Label, Button, Fieldset, Form, Checkboxes, TextInput} from "nhsuk-react-components" import {AppContext} from "../index" import ButtonList from "../components/common/buttonList" @@ -20,11 +20,37 @@ interface ConfigResponse { success: boolean } +interface ConfigDetails { + useSigningMock: boolean, + epsPrNumber: string, + signingPrNumber: string, + useProxygen: boolean +} + const ConfigPage: React.FC = () => { const {baseUrl, environment} = useContext(AppContext) const [configUpdateSuccess, setConfigUpdateSuccess] = useState(undefined) - const initialValues = {useSigningMock: false, epsPrNumber: "", signingPrNumber: "", useProxygen: false} + const [configValues, setConfigValues] = useState({useSigningMock: false, epsPrNumber: "", signingPrNumber: "", useProxygen: false}) + const initialValues = { + useSigningMock: configValues.useSigningMock, + epsPrNumber: configValues.epsPrNumber, + signingPrNumber: configValues.signingPrNumber, + useProxygen: configValues.useProxygen + } const isDevOrQa = isInternalDev(environment) || isInternalDevSandbox(environment) || isQa(environment) + + + useEffect(() => { + axiosInstance.get(`${baseUrl}getconfig`) + .then(response => { + if (response.status === 200) { + const configDetails = response.data + if (configDetails.useSigningMock && configDetails.epsPrNumber && configDetails.signingPrNumber && configDetails.useProxygen) { + setConfigValues(response.data) + } + } + }) + }, []) if (configUpdateSuccess !== undefined) { return <> @@ -35,13 +61,14 @@ const ConfigPage: React.FC = () => { } + return ( <> initialValues={initialValues} onSubmit={values => updateConfig(baseUrl, values, setConfigUpdateSuccess)} - > + enableReinitialize={true}> {formik => diff --git a/packages/tool/site/client/src/pages/loginPage.tsx b/packages/tool/site/client/src/pages/loginPage.tsx index 8adbf17780..88af0e4a79 100644 --- a/packages/tool/site/client/src/pages/loginPage.tsx +++ b/packages/tool/site/client/src/pages/loginPage.tsx @@ -1,4 +1,4 @@ -import React, {useContext, useState} from "react" +import React, {useContext, useEffect, useState} from "react" import {Button, Label} from "nhsuk-react-components" import {AppContext} from "../index" import {axiosInstance} from "../requests/axiosInstance" @@ -10,6 +10,12 @@ const LoginPage: React.FC<{separateAuth?: string}> = ({separateAuth}) => { const {baseUrl, environment} = useContext(AppContext) const [mockAuthSelected, setMockAuthSelected] = useState(false) + + useEffect(() => { + if (isInternalDev(environment)) { + setDefaultDevConfig(baseUrl) + } + }, []) if (isInt(environment)) { makeLoginRequest(baseUrl, separateAuth ? "user-separate" : "user-combined") @@ -55,4 +61,10 @@ const makeLoginRequest = async (baseUrl: string, authLevel: string) => { redirect(`${response.data.redirectUri}`) } +async function setDefaultDevConfig( + baseUrl: string +): Promise { + const defaultDevConfig = {useSigningMock: true, epsPrNumber: "", signingPrNumber: "", useProxygen: false} + await axiosInstance.post(`${baseUrl}config`, defaultDevConfig) +} export default LoginPage diff --git a/packages/tool/site/client/tests/pages/loginPage.spec.tsx b/packages/tool/site/client/tests/pages/loginPage.spec.tsx index 5fa2f9fa67..23fa9233d9 100644 --- a/packages/tool/site/client/tests/pages/loginPage.spec.tsx +++ b/packages/tool/site/client/tests/pages/loginPage.spec.tsx @@ -14,6 +14,7 @@ import MockAdapter from "axios-mock-adapter" const baseUrl = "baseUrl/" const loginUrl = `${baseUrl}login` +const configUrl = `${baseUrl}getconfig` const attendedAuthRedirectUrl = `https://attended-auth.com` const unattendedAuthRedirectUrl = `https://unattended-auth.com` @@ -22,7 +23,10 @@ jest.mock("../../src/browser/navigation") const mock = new MockAdapter(axiosInstance) -beforeEach(() => mock.reset()) +beforeEach(() => { + mock.reset() + mock.onAny(configUrl).reply(200) +}) afterEach(() => mock.reset()) test("Displays user/system options in internal-dev", async () => { diff --git a/packages/tool/site/server/src/routes/config/config.ts b/packages/tool/site/server/src/routes/config/config.ts index 87da3328fc..498b5dad8d 100644 --- a/packages/tool/site/server/src/routes/config/config.ts +++ b/packages/tool/site/server/src/routes/config/config.ts @@ -1,20 +1,39 @@ import Hapi, {RouteDefMethods} from "@hapi/hapi" -import {setSessionValue} from "../../services/session" +import {getSessionValue, setSessionValue} from "../../services/session" -export default { - method: "POST" as RouteDefMethods, - path: "/config", - handler: async (request: Hapi.Request, h: Hapi.ResponseToolkit): Promise => { - const payload = request.payload as { +export default [ + { + method: "POST" as RouteDefMethods, + path: "/config", + handler: async (request: Hapi.Request, h: Hapi.ResponseToolkit): Promise => { + const payload = request.payload as { useSigningMock: boolean, epsPrNumber: string, signingPrNumber: string, useProxygen: boolean } - setSessionValue("use_signing_mock", payload.useSigningMock, request) - setSessionValue("eps_pr_number", payload.epsPrNumber, request) - setSessionValue("signing_pr_number", payload.signingPrNumber, request) - setSessionValue("use_proxygen", payload.useProxygen, request) - return h.response({success: true}).code(200) + setSessionValue("use_signing_mock", payload.useSigningMock, request) + setSessionValue("eps_pr_number", payload.epsPrNumber, request) + setSessionValue("signing_pr_number", payload.signingPrNumber, request) + setSessionValue("use_proxygen", payload.useProxygen, request) + return h.response({success: true}).code(200) + } + }, + { + method: "GET" as RouteDefMethods, + path: "/getconfig", + handler: async (request: Hapi.Request, h: Hapi.ResponseToolkit): Promise => { + const useSigningMock = getSessionValue("use_signing_mock", request) as boolean + const epsPrNumber = getSessionValue("eps_pr_number", request) as string + const signingPrNumber = getSessionValue("signing_pr_number", request) as string + const useProxygen = getSessionValue("use_proxygen", request) as boolean + const response = { + useSigningMock, + epsPrNumber, + signingPrNumber, + useProxygen + } + return h.response(response).code(200) + } } -} +] diff --git a/packages/tool/site/server/src/routes/index.ts b/packages/tool/site/server/src/routes/index.ts index 548962b1ea..7124c8665f 100644 --- a/packages/tool/site/server/src/routes/index.ts +++ b/packages/tool/site/server/src/routes/index.ts @@ -77,7 +77,7 @@ const routes: Array = isSandbox(CONFIG.environment) oauthCallbackRoute ] : [ - configRoutes, + ...configRoutes, ...authRoutes, ...apiRoutes, ...stateRoutes, From d1647427ed695cdd58a4e798317e0ec822c0869a Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 7 Feb 2025 08:17:03 +0000 Subject: [PATCH 19/22] check config correctly --- packages/tool/site/client/src/pages/configPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index ca79c564a2..390b74cf35 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -45,7 +45,10 @@ const ConfigPage: React.FC = () => { .then(response => { if (response.status === 200) { const configDetails = response.data - if (configDetails.useSigningMock && configDetails.epsPrNumber && configDetails.signingPrNumber && configDetails.useProxygen) { + if (configDetails.hasOwnProperty("useSigningMock") && + configDetails.hasOwnProperty("epsPrNumber") && + configDetails.hasOwnProperty("signingPrNumber") && + configDetails.hasOwnProperty("useProxygen")) { setConfigValues(response.data) } } From f7514e93d5b992cb633e58589bf85132c3850461 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:51:39 +0000 Subject: [PATCH 20/22] more tests --- .../__snapshots__/configPage.spec.tsx.snap | 18 +++++++++++- .../client/tests/pages/configPage.spec.tsx | 29 +++++++++++++++---- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap b/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap index f78f73be5b..2edb7bca29 100644 --- a/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap +++ b/packages/tool/site/client/tests/pages/__snapshots__/configPage.spec.tsx.snap @@ -1,6 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Displays config form 1`] = ` +exports[`Displays config form correctly in int 1`] = ` +"

+ +
+
+
+
+
+
+
+
+
+
+" +`; + +exports[`Displays config form correctly in internal dev 1`] = ` "

diff --git a/packages/tool/site/client/tests/pages/configPage.spec.tsx b/packages/tool/site/client/tests/pages/configPage.spec.tsx index b8476ad765..6b6722eaf4 100644 --- a/packages/tool/site/client/tests/pages/configPage.spec.tsx +++ b/packages/tool/site/client/tests/pages/configPage.spec.tsx @@ -7,12 +7,11 @@ import {AppContextValue} from "../../src" import {renderWithContext} from "../renderWithContext" import userEvent from "@testing-library/user-event" import {axiosInstance} from "../../src/requests/axiosInstance" -import {internalDev} from "../../src/services/environment" +import {int, internalDev} from "../../src/services/environment" import ConfigPage from "../../src/pages/configPage" import {MemoryRouter} from "react-router-dom" const baseUrl = "baseUrl/" -const context: AppContextValue = {baseUrl, environment: internalDev} const configUrl = `${baseUrl}config` @@ -21,25 +20,43 @@ const mock = new MockAdapter(axiosInstance) beforeEach(() => mock.reset()) afterEach(() => mock.reset()) -test("Displays config form", async () => { - const container = await renderPage() +test("Displays config form correctly in internal dev", async () => { + const context: AppContextValue = {baseUrl, environment: internalDev} + const container = await renderPage(context) expect(screen.getByText("Config")).toBeTruthy() + expect(screen.getByText("EPS PR Number")).toBeTruthy() + expect(screen.getByText("Use Proxygen deployed APIs")).toBeTruthy() + expect(screen.getByText("Use Signing Mock")).toBeTruthy() + expect(screen.getByText("Signing PR Number")).toBeTruthy() + expect(pretty(container.innerHTML)).toMatchSnapshot() +}) + +test("Displays config form correctly in int", async () => { + const context: AppContextValue = {baseUrl, environment: int} + const container = await renderPage(context) + + expect(screen.getByText("Config")).toBeTruthy() + expect(screen.queryByText("EPS PR Number")).toBeNull() + expect(screen.getByText("Use Proxygen deployed APIs")).toBeTruthy() + expect(screen.queryByText("Use Signing Mock")).toBeNull() + expect(screen.queryByText("Signing PR Number")).toBeNull() expect(pretty(container.innerHTML)).toMatchSnapshot() }) test("Displays config update result", async () => { + const context: AppContextValue = {baseUrl, environment: internalDev} mock.onAny(configUrl).reply(200, { success: true }) - const container = await renderPage() + const container = await renderPage(context) userEvent.click(screen.getByText("Save")) await waitFor(() => screen.getByText(/Config Saved/)) expect(pretty(container.innerHTML)).toMatchSnapshot() }) -async function renderPage() { +async function renderPage(context) { const {container} = renderWithContext(, context) await waitFor(() => screen.getByText("Config")) return container From d4fe378500ea72345ffb39e6a2968cba0759ca37 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 7 Feb 2025 12:29:01 +0000 Subject: [PATCH 21/22] test for header --- .../__snapshots__/pageHeader.spec.tsx.snap | 39 +++++++++++++++++++ .../tests/components/pageHeader.spec.tsx | 32 +++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 packages/tool/site/client/tests/components/__snapshots__/pageHeader.spec.tsx.snap create mode 100644 packages/tool/site/client/tests/components/pageHeader.spec.tsx diff --git a/packages/tool/site/client/tests/components/__snapshots__/pageHeader.spec.tsx.snap b/packages/tool/site/client/tests/components/__snapshots__/pageHeader.spec.tsx.snap new file mode 100644 index 0000000000..65714baee2 --- /dev/null +++ b/packages/tool/site/client/tests/components/__snapshots__/pageHeader.spec.tsx.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Header renders when logged in 1`] = ` +"" +`; + +exports[`Header renders when not logged in 1`] = ` +"" +`; diff --git a/packages/tool/site/client/tests/components/pageHeader.spec.tsx b/packages/tool/site/client/tests/components/pageHeader.spec.tsx new file mode 100644 index 0000000000..b4c155bf59 --- /dev/null +++ b/packages/tool/site/client/tests/components/pageHeader.spec.tsx @@ -0,0 +1,32 @@ +import {render, waitFor} from "@testing-library/react" +import {screen} from "@testing-library/dom" +import pretty from "pretty" +import * as React from "react" +import {PageHeader} from "../../src/components/pageHeader" +import {expect} from "@jest/globals" +import {MemoryRouter} from "react-router-dom" + +test("Header renders when logged in", async () => { + const {container} = render( + + + + ) + + expect(screen.getByText("My Prescriptions")).toBeTruthy() + + expect(pretty(container.innerHTML)).toMatchSnapshot() +}) + +test("Header renders when not logged in", async () => { + const {container} = render( + + + + ) + + expect(screen.queryByText("My Prescriptions")).toBeNull() + + expect(pretty(container.innerHTML)).toMatchSnapshot() +}) + From 09a92eeeac0e5cb53c8b0abf1cf67efdd56a0d28 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 7 Feb 2025 12:40:52 +0000 Subject: [PATCH 22/22] sonar tweaks --- packages/tool/site/client/src/components/pageHeader.tsx | 2 +- packages/tool/site/client/src/pages/configPage.tsx | 2 +- packages/tool/site/client/tests/components/pageHeader.spec.tsx | 2 +- .../tool/site/server/src/services/communication/eps-client.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/tool/site/client/src/components/pageHeader.tsx b/packages/tool/site/client/src/components/pageHeader.tsx index f9dbbf63aa..2a440dbd9c 100644 --- a/packages/tool/site/client/src/components/pageHeader.tsx +++ b/packages/tool/site/client/src/components/pageHeader.tsx @@ -2,7 +2,7 @@ import * as React from "react" import {useContext} from "react" import {Header, Images} from "nhsuk-react-components" import {AppContext} from "../index" -import {isInternalDev, isInternalDevSandbox, isQa, isSandbox} from "../services/environment" +import {isSandbox} from "../services/environment" import SessionTimer from "./sessionTimer" import styled from "styled-components" diff --git a/packages/tool/site/client/src/pages/configPage.tsx b/packages/tool/site/client/src/pages/configPage.tsx index 390b74cf35..324c2d3381 100644 --- a/packages/tool/site/client/src/pages/configPage.tsx +++ b/packages/tool/site/client/src/pages/configPage.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import {useContext, useState, useEffect, useCallback} from "react" +import {useContext, useState, useEffect} from "react" import {Label, Button, Fieldset, Form, Checkboxes, TextInput} from "nhsuk-react-components" import {AppContext} from "../index" import ButtonList from "../components/common/buttonList" diff --git a/packages/tool/site/client/tests/components/pageHeader.spec.tsx b/packages/tool/site/client/tests/components/pageHeader.spec.tsx index b4c155bf59..b2feee0585 100644 --- a/packages/tool/site/client/tests/components/pageHeader.spec.tsx +++ b/packages/tool/site/client/tests/components/pageHeader.spec.tsx @@ -1,4 +1,4 @@ -import {render, waitFor} from "@testing-library/react" +import {render} from "@testing-library/react" import {screen} from "@testing-library/dom" import pretty from "pretty" import * as React from "react" diff --git a/packages/tool/site/server/src/services/communication/eps-client.ts b/packages/tool/site/server/src/services/communication/eps-client.ts index d563b8120e..0f5327b838 100644 --- a/packages/tool/site/server/src/services/communication/eps-client.ts +++ b/packages/tool/site/server/src/services/communication/eps-client.ts @@ -69,7 +69,7 @@ interface GetEpsResponseParameters { class EpsClient { private request: Hapi.Request - private axiosInstance: AxiosInstance + private readonly axiosInstance: AxiosInstance constructor(request: Hapi.Request) { this.request = request