Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AEA-3534 Modify Spine client code so that it doesn't call Spine if the Certificate is not configured #166

Merged
30 changes: 30 additions & 0 deletions packages/getMyPrescriptions/src/getMyPrescriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,36 @@ const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayPro
const spineClient = createSpineClient()

try {
const isCertificateConfigured = spineClient.isCertificateConfigured()
if (!isCertificateConfigured) {
const errorResponseBody = {
resourceType: "OperationOutcome",
issue: [
{
code: "security",
severity: "fatal",
details: {
coding: [
{
system: "https://fhir.nhs.uk/CodeSystem/http-error-codes",
code: "SERVER_ERROR",
display: "500: The Server has encountered an error processing the request."
}
]
},
diagnostics: "Spine certificate is not configured"
}
]
}
return {
statusCode: 500,
body: JSON.stringify(errorResponseBody),
headers: {
"Content-Type": "application/fhir+json",
"Cache-Control": "no-cache"
}
}
}
const returnData = await spineClient.getPrescriptions(event.headers, logger)
const resBody = returnData.data
resBody.id = xRequestId
Expand Down
37 changes: 37 additions & 0 deletions packages/getMyPrescriptions/tests/test-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ const responseStatus500 = {
]
}

const responseNotConfCertStatus500 = {
resourceType: "OperationOutcome",
issue: [
{
code: "security",
severity: "fatal",
details: {
coding: [
{
system: "https://fhir.nhs.uk/CodeSystem/http-error-codes",
code: "SERVER_ERROR",
display: "500: The Server has encountered an error processing the request."
}
]
},
diagnostics: "Spine certificate is not configured"
}
]
}

type spineFailureTestData = {
httpResponseCode: number
spineStatusCode: string
Expand Down Expand Up @@ -210,6 +230,23 @@ describe("Unit test for app handler", function () {
})
expect(JSON.parse(result.body)).toEqual(responseStatus500)
})

it("return error when the certificate is not configured", async () => {
process.env.SpinePublicCertificate = "ChangeMe"
process.env.SpinePrivateKey = "ChangeMe"
process.env.SpineCAChain = "ChangeMe"

mock.onGet("https://live/mm/patientfacingprescriptions").reply(500, {resourceType: "Bundle"})
const event: APIGatewayProxyEvent = JSON.parse(exampleEvent)
const result: APIGatewayProxyResult = await handler(event, dummyContext)

expect(result.statusCode).toBe(500)
expect(result.headers).toEqual({
"Content-Type": "application/fhir+json",
"Cache-Control": "no-cache"
})
expect(JSON.parse(result.body)).toEqual(responseNotConfCertStatus500)
})
})

export {}
9 changes: 9 additions & 0 deletions packages/spineClient/src/live-spine-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,13 @@ export class LiveSpineClient implements SpineClient {
return serviceHealthCheck(process.env.healthCheckUrl, logger, new Agent())
}
}

isCertificateConfigured(): boolean {
// Check if the required certificate-related environment variables are defined
return (
process.env.SpinePublicCertificate !== "ChangeMe" &&
process.env.SpinePrivateKey !== "ChangeMe" &&
process.env.SpineCAChain !== "ChangeMe"
)
}
}
5 changes: 5 additions & 0 deletions packages/spineClient/src/sandbox-spine-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ export class SandboxSpineClient implements SpineClient {
// This is not implemented as sandbox lambda does not use this code
throw new Error("INTERACTION_NOT_SUPPORTED_BY_SANDBOX")
}

isCertificateConfigured(): boolean {
// In the sandbox environment, assume the certificate is always configured
return true
}
}
1 change: 1 addition & 0 deletions packages/spineClient/src/spine-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {AxiosResponse} from "axios"
export interface SpineClient {
getStatus(logger: Logger): Promise<StatusCheckResponse>
getPrescriptions(inboundHeaders: APIGatewayProxyEventHeaders, logger: Logger): Promise<AxiosResponse>
isCertificateConfigured(): boolean
}

export function createSpineClient(): SpineClient {
Expand Down
22 changes: 20 additions & 2 deletions packages/statusLambda/src/statusLambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,29 @@ const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayPro

const spineClient = createSpineClient()

const spineStatus = await spineClient.getStatus(logger)

const commitId = process.env.COMMIT_ID
const versionNumber = process.env.VERSION_NUMBER

const isCertificateConfigured = spineClient.isCertificateConfigured()

if (!isCertificateConfigured) {
return {
statusCode: 200,
body: JSON.stringify({
commitId: commitId,
versionNumber: versionNumber,
status: "pass",
message: "Spine certificate is not configured"
}),
headers: {
"Content-Type": "application/health+json",
"Cache-Control": "no-cache"
}
}
}

const spineStatus = await spineClient.getStatus(logger)

return {
statusCode: 200,
body: JSON.stringify({
Expand Down
84 changes: 84 additions & 0 deletions packages/statusLambda/tests/test-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const mock = new MockAdapter(axios)
const dummyContext = ContextExamples.helloworldContext

describe("Unit test for status check", function () {
let originalEnv: {[key: string]: string | undefined}
afterEach(() => {
process.env = {...originalEnv}
mock.reset()
})

Expand Down Expand Up @@ -76,6 +78,19 @@ describe("Unit test for status check", function () {
})
})

it("checks if the certificate is always configured for the sandbox", async () => {
process.env.TargetSpineServer = "sandbox"
process.env.SpinePublicCertificate = "ChangeMe"
process.env.SpinePrivateKey = "ChangeMe"
process.env.SpineCAChain = "ChangeMe"

const result: APIGatewayProxyResult = await handler(mockAPIGatewayProxyEvent, dummyContext)

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
expect(result_body).not.toHaveProperty("message")
})

it("returns success when spine check succeeds", async () => {
mock.onGet("https://live/healthcheck").reply(200, {})
process.env.TargetSpineServer = "live"
Expand Down Expand Up @@ -143,4 +158,73 @@ describe("Unit test for status check", function () {
expect(result_body.spineStatus.timeout).toEqual("true")
expect(result_body.spineStatus.responseCode).toEqual(500)
})

it("returns success when Spine check succeeds and the certificate is not configured", async () => {
mock.onGet("https://live/healthcheck").reply(200, {})
process.env.TargetSpineServer = "live"
process.env.SpinePublicCertificate = "ChangeMe"
kris-szlapa marked this conversation as resolved.
Show resolved Hide resolved
process.env.SpinePrivateKey = "ChangeMe"
process.env.SpineCAChain = "ChangeMe"

const result: APIGatewayProxyResult = await handler(mockAPIGatewayProxyEvent, dummyContext)

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
expect(result_body.status).toEqual("pass")
expect(result_body.message).toEqual("Spine certificate is not configured")
})

it("returns success when Spine check succeeds without SpinePublicCertificate", async () => {
mock.onGet("https://live/healthcheck").reply(200, {})
process.env.TargetSpineServer = "live"
process.env.SpinePublicCertificate = "ChangeMe"

const result: APIGatewayProxyResult = await handler(mockAPIGatewayProxyEvent, dummyContext)

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
expect(result_body.status).toEqual("pass")
expect(result_body.message).toEqual("Spine certificate is not configured")
})

it("returns success when Spine check succeeds without SpinePrivateKey", async () => {
mock.onGet("https://live/healthcheck").reply(200, {})
process.env.TargetSpineServer = "live"
process.env.SpinePrivateKey = "ChangeMe"

const result: APIGatewayProxyResult = await handler(mockAPIGatewayProxyEvent, dummyContext)

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
expect(result_body.status).toEqual("pass")
expect(result_body.message).toEqual("Spine certificate is not configured")
})

it("returns success when Spine check succeeds without SpineCAChain", async () => {
mock.onGet("https://live/healthcheck").reply(200, {})
process.env.TargetSpineServer = "live"
process.env.SpineCAChain = "ChangeMe"

const result: APIGatewayProxyResult = await handler(mockAPIGatewayProxyEvent, dummyContext)

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
expect(result_body.status).toEqual("pass")
expect(result_body.message).toEqual("Spine certificate is not configured")
})

it("returns failure when Spine check fails and the certificate is configured", async () => {
mock.onGet("https://live/healthcheck").reply(500, {})
process.env.TargetSpineServer = "live"

const result: APIGatewayProxyResult = await handler(mockAPIGatewayProxyEvent, dummyContext)

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
expect(result_body).not.toHaveProperty("message")
expect(result_body.status).toEqual("error")
expect(result_body.spineStatus.status).toEqual("error")
expect(result_body.spineStatus.timeout).toEqual("false")
expect(result_body.spineStatus.responseCode).toEqual(500)
})
})