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 = await spineClient.isCertificateConfigured()
kris-szlapa marked this conversation as resolved.
Show resolved Hide resolved
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)) as APIGatewayProxyResult

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())
}
}

async isCertificateConfigured(): Promise<boolean> {
kris-szlapa marked this conversation as resolved.
Show resolved Hide resolved
// 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")
}

async isCertificateConfigured(): Promise<boolean> {
kris-szlapa marked this conversation as resolved.
Show resolved Hide resolved
// 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(): Promise<boolean>
kris-szlapa marked this conversation as resolved.
Show resolved Hide resolved
}

export function createSpineClient(): SpineClient {
Expand Down
21 changes: 19 additions & 2 deletions packages/statusLambda/src/statusLambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,28 @@ 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 = await spineClient.isCertificateConfigured()
kris-szlapa marked this conversation as resolved.
Show resolved Hide resolved

if (!isCertificateConfigured) {
return {
statusCode: 200,
body: JSON.stringify({
commitId: commitId,
versionNumber: versionNumber,
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
53 changes: 53 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,22 @@ describe("Unit test for status check", function () {
})
})

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

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

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 +161,39 @@ 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
)) as APIGatewayProxyResult

expect(result.statusCode).toEqual(200)
const result_body = JSON.parse(result.body)
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
)) as APIGatewayProxyResult

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)
})
})