Skip to content

Commit

Permalink
Add the fetchPrescriptionDataLambda using the LambdaFunction class
Browse files Browse the repository at this point in the history
  • Loading branch information
kris-szlapa committed Nov 4, 2024
1 parent 4e2973a commit 0006415
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 4 deletions.
98 changes: 98 additions & 0 deletions packages/cdk/resources/LambdaFunction/fetchPrescriptionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {APIGatewayProxyHandler} from "aws-lambda"
import axios from "axios"
import {DynamoDB} from "aws-sdk"

const APIGEE_BASE_URL = process.env.APIGEE_BASE_URL || "https://internal-dev.api.service.nhs.uk"
const TABLE_NAME = process.env.TABLE_NAME || "DefaultTableName"
const TOKEN_KEY = "apigeeAccessToken"

const dynamoDb = new DynamoDB.DocumentClient()

/**
* Helper function to fetch or refresh the token from DynamoDB.
* @returns {Promise<string>} The access token.
*/
async function getToken(): Promise<string> {
// Fetch token from DynamoDB
const result = await dynamoDb.get({
TableName: TABLE_NAME,
Key: {id: TOKEN_KEY}
}).promise()

const tokenData = result.Item

// Check if a valid token exists
if (tokenData && tokenData.expiry > Date.now()) {
return tokenData.token
}

// Otherwise, generate a new token
const newToken = await generateNewToken()
const expiryTime = Date.now() + 60 * 60 * 1000 // 1 hour expiry

// Store the new token in DynamoDB
await dynamoDb.put({
TableName: TABLE_NAME,
Item: {
id: TOKEN_KEY,
token: newToken,
expiry: expiryTime
}
}).promise()

return newToken
}

/**
* Generate a new access token.
* This could be a call to a secondary Lambda or an external provider.
* @returns {Promise<string>} The generated token.
*/
async function generateNewToken(): Promise<string> {
// Placeholder for actual token generation logic
return "new-generated-token"
}

/**
* Lambda function handler to fetch prescription data.
* @param event The API Gateway request event.
* @returns {Promise<any>} The response from Apigee.
*/
export const handler: APIGatewayProxyHandler = async (event) => {
const prescriptionId = event.pathParameters?.id

if (!prescriptionId) {
return {
statusCode: 400,
body: JSON.stringify({error: "Prescription ID is required"})
}
}

try {
const token = await getToken()

const url = `${APIGEE_BASE_URL}/clinical-prescription-tracker/${prescriptionId}`
const response = await axios.get(url, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
}
})

return {
statusCode: 200,
body: JSON.stringify(response.data)
}
} catch (error) {
console.error("Error fetching prescription data:", error)

const errorMessage = error.response
? `Apigee responded with ${error.response.status}: ${error.response.statusText}`
: "Unexpected error occurred while fetching data"

return {
statusCode: 500,
body: JSON.stringify({error: errorMessage})
}
}
}
34 changes: 30 additions & 4 deletions packages/cdk/stacks/StatelessResourcesStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import {
ViewerProtocolPolicy
} from "aws-cdk-lib/aws-cloudfront"
import {RestApiOrigin, S3BucketOrigin} from "aws-cdk-lib/aws-cloudfront-origins"
import {LambdaIntegration} from "aws-cdk-lib/aws-apigateway"
import {Bucket} from "aws-cdk-lib/aws-s3"

import {RestApiGateway} from "../resources/RestApiGateway"
import {CloudfrontFunction} from "../resources/Cloudfront/CloudfrontFunction"
import {CloudfrontDistribution} from "../resources/CloudfrontDistribution"
import {LambdaFunction} from "../resources/LambdaFunction"
import {getDefaultLambdaOptions} from "../resources/LambdaFunction/helpers"
import {nagSuppressions} from "../nagSuppressions"

export interface StatelessResourcesStackProps extends StackProps {
Expand All @@ -29,12 +32,12 @@ export interface StatelessResourcesStackProps extends StackProps {
}

/**
* Clinical Prescription Tracker UI Stateful Resources
* Clinical Prescription Tracker UI Stateless Resources
*/

export class StatelessResourcesStack extends Stack {
public constructor(scope: App, id: string, props: StatelessResourcesStackProps){
public constructor(scope: App, id: string, props: StatelessResourcesStackProps) {
super(scope, id, props)

// Context
Expand All @@ -51,7 +54,30 @@ export class StatelessResourcesStack extends Stack {
stackName: props.stackName
})

// --- Methods & Resources
// --- Lambda for fetchPrescriptionData
const lambdaOptions = getDefaultLambdaOptions({
functionName: `${props.serviceName}-fetchPrescriptionData`,
packageBasePath: "packages/cdk/resources/LambdaFunction",
entryPoint: "fetchPrescriptionData.ts"
})

const fetchPrescriptionDataLambda = new LambdaFunction(this, "FetchPrescriptionDataLambda", {
...lambdaOptions,
serviceName: props.serviceName,
stackName: props.stackName,
lambdaEnvironmentVariables: {
APIGEE_BASE_URL: process.env.APIGEE_BASE_URL || "https://internal-dev.api.service.nhs.uk",
TABLE_NAME: process.env.TABLE_NAME || "DefaultTableName"
}
})

// --- API Gateway Route for /api/prescription-search/{id}
const api = apiGateway.restApiGateway
const prescriptionSearchResource = api.root
.addResource("api")
.addResource("prescription-search")
.addResource("{id}")
prescriptionSearchResource.addMethod("GET", new LambdaIntegration(fetchPrescriptionDataLambda.lambda))

// - Cloudfront
// --- Origins
Expand Down Expand Up @@ -137,7 +163,7 @@ export class StatelessResourcesStack extends Stack {
origin: staticContentBucketOrigin,
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
functionAssociations:[
functionAssociations: [
{
function: s3404UriRewriteFunction.function,
eventType: FunctionEventType.VIEWER_REQUEST
Expand Down

0 comments on commit 0006415

Please sign in to comment.