Skip to content

Commit ad5e7bd

Browse files
authored
Find by id endpoint (#8)
* Update ESLint config to use glob patterns for ignored directories * Implement get task functionality with corresponding tests and update Lambda stack * Add Get Task functionality to Infrastructure Guide
1 parent d63a699 commit ad5e7bd

8 files changed

Lines changed: 677 additions & 11 deletions

File tree

docs/InfrastructureGuide.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,40 @@ const tableName = process.env.TASK_TABLE_NAME;
425425
- **DynamoDB**: Read access (Scan) to the Task table
426426
- **CloudWatch Logs**: Write access to its log group
427427

428+
#### Get Task Function
429+
430+
**Resource Type**: AWS Lambda Function
431+
432+
**Configuration**:
433+
434+
- **Function Name**: `{app-name}-get-task-{env}`
435+
- **Runtime**: Node.js 24.x
436+
- **Handler**: `handler` (bundled with esbuild)
437+
- **Memory**: 256 MB
438+
- **Timeout**: 10 seconds
439+
- **Log Format**: JSON (structured logging)
440+
- **Bundling**: Automatic TypeScript compilation with esbuild
441+
- **Environment Variables**:
442+
- `TASKS_TABLE`: DynamoDB table name
443+
- `ENABLE_LOGGING`: Logging enabled flag (from `CDK_APP_ENABLE_LOGGING`)
444+
- `LOG_LEVEL`: Minimum log level (from `CDK_APP_LOGGING_LEVEL`)
445+
- `LOG_FORMAT`: Log output format (from `CDK_APP_LOGGING_FORMAT`)
446+
447+
**CloudWatch Logs**:
448+
449+
- **Log Group**: `/aws/lambda/{app-name}-get-task-{env}`
450+
- **Log Retention**:
451+
- `prd`: 30 days
452+
- Other environments: 7 days
453+
- **Removal Policy**:
454+
- `prd`: `RETAIN` (logs preserved on stack deletion)
455+
- Other environments: `DESTROY` (logs deleted on stack deletion)
456+
457+
**IAM Permissions**:
458+
459+
- **DynamoDB**: Read access (GetItem) to the Task table
460+
- **CloudWatch Logs**: Write access to its log group
461+
428462
#### Create Task Function
429463

430464
**Resource Type**: AWS Lambda Function
@@ -482,6 +516,14 @@ const tableName = process.env.TASK_TABLE_NAME;
482516
- Integration: Lambda proxy integration with List Tasks Function
483517
- Response: JSON array of tasks
484518

519+
- **GET /tasks/{taskId}**: Get a specific task by ID
520+
- Integration: Lambda proxy integration with Get Task Function
521+
- Path Parameter: `taskId` - The unique identifier of the task
522+
- Response: JSON object with the requested task
523+
- Error Responses:
524+
- 404 Not Found: Task ID does not exist or path parameter is missing
525+
- 500 Internal Server Error: Failed to retrieve task
526+
485527
- **POST /tasks**: Create a new task
486528
- Integration: Lambda proxy integration with Create Task Function
487529
- Request Body: JSON object with task properties (title, description, status)
@@ -492,6 +534,7 @@ const tableName = process.env.TASK_TABLE_NAME;
492534
- `ApiUrl`: The API Gateway endpoint URL (e.g., `https://abc123.execute-api.us-east-1.amazonaws.com/dev/`)
493535
- `ApiId`: The API Gateway ID
494536
- `ListTasksFunctionArn`: The List Tasks Lambda function ARN
537+
- `GetTaskFunctionArn`: The Get Task Lambda function ARN
495538
- `CreateTaskFunctionArn`: The Create Task Lambda function ARN
496539

497540
**Logging Configuration**:

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import eslintConfigPrettier from 'eslint-config-prettier/flat';
99

1010
export default defineConfig(
1111
{
12-
ignores: ['dist', 'coverage', 'cdk.out'],
12+
ignores: ['**/dist', '**/coverage', '**/cdk.out'],
1313
},
1414
{
1515
extends: [js.configs.recommended, ...tseslint.configs.recommended],

infrastructure/stacks/lambda-stack.test.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ describe('LambdaStack', () => {
5757
});
5858
});
5959

60+
it('should create a get task Lambda function', () => {
61+
template.hasResourceProperties('AWS::Lambda::Function', {
62+
FunctionName: 'lambda-starter-get-task-dev',
63+
Runtime: 'nodejs24.x',
64+
Handler: 'handler',
65+
Timeout: 10,
66+
MemorySize: 256,
67+
});
68+
});
69+
6070
it('should create a create task Lambda function', () => {
6171
template.hasResourceProperties('AWS::Lambda::Function', {
6272
FunctionName: 'lambda-starter-create-task-dev',
@@ -88,12 +98,18 @@ describe('LambdaStack', () => {
8898
});
8999

90100
it('should create a /tasks resource', () => {
91-
template.resourceCountIs('AWS::ApiGateway::Resource', 1);
101+
template.resourceCountIs('AWS::ApiGateway::Resource', 2);
92102
template.hasResourceProperties('AWS::ApiGateway::Resource', {
93103
PathPart: 'tasks',
94104
});
95105
});
96106

107+
it('should create a /tasks/{taskId} resource', () => {
108+
template.hasResourceProperties('AWS::ApiGateway::Resource', {
109+
PathPart: '{taskId}',
110+
});
111+
});
112+
97113
it('should create a GET method on /tasks', () => {
98114
template.hasResourceProperties('AWS::ApiGateway::Method', {
99115
HttpMethod: 'GET',
@@ -173,31 +189,39 @@ describe('LambdaStack', () => {
173189
it('should export API URL', () => {
174190
template.hasOutput('ApiUrl', {
175191
Export: {
176-
Name: 'dev-tasks-api-url',
192+
Name: 'lambda-starter-tasks-api-url-dev',
177193
},
178194
});
179195
});
180196

181197
it('should export API ID', () => {
182198
template.hasOutput('ApiId', {
183199
Export: {
184-
Name: 'dev-tasks-api-id',
200+
Name: 'lambda-starter-tasks-api-id-dev',
185201
},
186202
});
187203
});
188204

189205
it('should export Lambda function ARN', () => {
190206
template.hasOutput('ListTasksFunctionArn', {
191207
Export: {
192-
Name: 'dev-list-tasks-function-arn',
208+
Name: 'lambda-starter-list-tasks-function-arn-dev',
193209
},
194210
});
195211
});
196212

197213
it('should export create task function ARN', () => {
198214
template.hasOutput('CreateTaskFunctionArn', {
199215
Export: {
200-
Name: 'dev-create-task-function-arn',
216+
Name: 'lambda-starter-create-task-function-arn-dev',
217+
},
218+
});
219+
});
220+
221+
it('should export get task function ARN', () => {
222+
template.hasOutput('GetTaskFunctionArn', {
223+
Export: {
224+
Name: 'lambda-starter-get-task-function-arn-dev',
201225
},
202226
});
203227
});

infrastructure/stacks/lambda-stack.ts

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export class LambdaStack extends cdk.Stack {
5656
*/
5757
public readonly listTasksFunction: NodejsFunction;
5858

59+
/**
60+
* The get task Lambda function.
61+
*/
62+
public readonly getTaskFunction: NodejsFunction;
63+
5964
/**
6065
* The create task Lambda function.
6166
*/
@@ -95,6 +100,37 @@ export class LambdaStack extends cdk.Stack {
95100
// Grant the Lambda function read access to the DynamoDB table
96101
props.taskTable.grantReadData(this.listTasksFunction);
97102

103+
// Create the get task Lambda function
104+
this.getTaskFunction = new NodejsFunction(this, 'GetTaskFunction', {
105+
functionName: `${props.appName}-get-task-${props.envName}`,
106+
runtime: lambda.Runtime.NODEJS_24_X,
107+
handler: 'handler',
108+
entry: path.join(__dirname, '../../src/handlers/get-task.ts'),
109+
environment: {
110+
TASKS_TABLE: props.taskTable.tableName,
111+
ENABLE_LOGGING: props.enableLogging.toString(),
112+
LOG_LEVEL: props.loggingLevel,
113+
LOG_FORMAT: props.loggingFormat,
114+
},
115+
timeout: cdk.Duration.seconds(10),
116+
memorySize: 256,
117+
bundling: {
118+
minify: true,
119+
sourceMap: true,
120+
},
121+
loggingFormat: lambda.LoggingFormat.JSON,
122+
applicationLogLevelV2: lambda.ApplicationLogLevel.INFO,
123+
systemLogLevelV2: lambda.SystemLogLevel.INFO,
124+
logGroup: new logs.LogGroup(this, 'GetTaskFunctionLogGroup', {
125+
logGroupName: `/aws/lambda/${props.appName}-get-task-${props.envName}`,
126+
retention: props.envName === 'prd' ? logs.RetentionDays.ONE_MONTH : logs.RetentionDays.ONE_WEEK,
127+
removalPolicy: props.envName === 'prd' ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
128+
}),
129+
});
130+
131+
// Grant the Lambda function read access to the DynamoDB table
132+
props.taskTable.grantReadData(this.getTaskFunction);
133+
98134
// Create the create task Lambda function
99135
this.createTaskFunction = new NodejsFunction(this, 'CreateTaskFunction', {
100136
functionName: `${props.appName}-create-task-${props.envName}`,
@@ -151,32 +187,45 @@ export class LambdaStack extends cdk.Stack {
151187
// Add POST method to /tasks
152188
tasksResource.addMethod('POST', new apigateway.LambdaIntegration(this.createTaskFunction));
153189

190+
// Create /tasks/{taskId} resource
191+
const taskResource = tasksResource.addResource('{taskId}');
192+
193+
// Add GET method to /tasks/{taskId}
194+
taskResource.addMethod('GET', new apigateway.LambdaIntegration(this.getTaskFunction));
195+
154196
// Output the API URL
155197
new cdk.CfnOutput(this, 'ApiUrl', {
156198
value: this.api.url,
157199
description: 'URL of the Tasks API',
158-
exportName: `${props.envName}-tasks-api-url`,
200+
exportName: `${props.appName}-tasks-api-url-${props.envName}`,
159201
});
160202

161203
// Output the API Gateway ID
162204
new cdk.CfnOutput(this, 'ApiId', {
163205
value: this.api.restApiId,
164206
description: 'ID of the Tasks API',
165-
exportName: `${props.envName}-tasks-api-id`,
207+
exportName: `${props.appName}-tasks-api-id-${props.envName}`,
166208
});
167209

168210
// Output the list tasks function ARN
169211
new cdk.CfnOutput(this, 'ListTasksFunctionArn', {
170212
value: this.listTasksFunction.functionArn,
171213
description: 'ARN of the list tasks Lambda function',
172-
exportName: `${props.envName}-list-tasks-function-arn`,
214+
exportName: `${props.appName}-list-tasks-function-arn-${props.envName}`,
215+
});
216+
217+
// Output the get task function ARN
218+
new cdk.CfnOutput(this, 'GetTaskFunctionArn', {
219+
value: this.getTaskFunction.functionArn,
220+
description: 'ARN of the get task Lambda function',
221+
exportName: `${props.appName}-get-task-function-arn-${props.envName}`,
173222
});
174223

175224
// Output the create task function ARN
176225
new cdk.CfnOutput(this, 'CreateTaskFunctionArn', {
177226
value: this.createTaskFunction.functionArn,
178227
description: 'ARN of the create task Lambda function',
179-
exportName: `${props.envName}-create-task-function-arn`,
228+
exportName: `${props.appName}-create-task-function-arn-${props.envName}`,
180229
});
181230
}
182231
}

0 commit comments

Comments
 (0)