Skip to content
This repository was archived by the owner on Aug 30, 2022. It is now read-only.

Commit 0224acc

Browse files
FivemindzFivemindz
Fivemindz
authored and
Fivemindz
committed
built create/retrieve/update/delete endpoints-added signed url to s3 functionality-tests successful with React frontend
1 parent 9c4d328 commit 0224acc

File tree

11 files changed

+973
-460
lines changed

11 files changed

+973
-460
lines changed

course-04/project/c4-final-project-starter-code/backend/package-lock.json

Lines changed: 770 additions & 316 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

course-04/project/c4-final-project-starter-code/backend/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"dependencies": {
66
"@types/axios": "^0.14.0",
77
"@types/http-errors": "^1.6.1",
8-
"aws-xray-sdk": "^2.2.0",
8+
"aws-xray-sdk": "^3.2.0",
99
"axios": "^0.18.0",
1010
"http-errors": "^1.7.2",
1111
"jsonwebtoken": "^8.5.1",
@@ -18,13 +18,14 @@
1818
"@types/aws-lambda": "^8.10.17",
1919
"@types/jsonwebtoken": "^8.3.2",
2020
"@types/node": "^10.7.0",
21+
"@types/uuid": "^9.0.0",
2122
"aws-sdk": "^2.433.0",
2223
"serverless-aws-documentation": "^1.1.0",
2324
"serverless-iam-roles-per-function": "^3.2.0-e97ab49",
2425
"serverless-plugin-canary-deployments": "^0.4.7",
2526
"serverless-plugin-tracing": "^2.0.0",
2627
"serverless-reqvalidator-plugin": "^1.0.3",
27-
"serverless-webpack": "^5.2.0",
28+
"serverless-webpack": "^5.11.0",
2829
"ts-loader": "^5.3.3",
2930
"typescript": "^4.6.4",
3031
"webpack": "^4.29.6"

course-04/project/c4-final-project-starter-code/backend/serverless.yml

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ provider:
2525
environment:
2626
TODOS_TABLE: Todos-${self:provider.stage}
2727
TODOS_CREATED_AT_INDEX: CreatedAtIndex
28-
ATTACHMENT_S3_BUCKET: serverless-c4-todo-images-${self:provider.stage}
28+
ATTACHMENT_S3_BUCKET: serverless-c4-todo-attachments-${self:provider.stage}
2929
SIGNED_URL_EXPIRATION: 300
3030

3131
logs:
@@ -130,6 +130,7 @@ functions:
130130
- Effect: Allow
131131
Action:
132132
- dynamodb:PutItem
133+
- dynamodb:UpdateItem
133134
Resource: arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.TODOS_TABLE}
134135

135136
resources:
@@ -171,12 +172,36 @@ resources:
171172

172173
# TODO: Complete the bucket properties below.
173174
# Do not forget to add the CorsConfiguration propoerty
174-
# AttachmentsBucket:
175-
# Type: AWS::S3::Bucket
176-
# Properties:
175+
AttachmentsBucket:
176+
Type: AWS::S3::Bucket
177+
Properties:
178+
BucketName: ${self:provider.environment.ATTACHMENT_S3_BUCKET}
179+
CorsConfiguration:
180+
CorsRules:
181+
- AllowedOrigins:
182+
- '*'
183+
AllowedHeaders:
184+
- '*'
185+
AllowedMethods:
186+
- GET
187+
- PUT
188+
- POST
189+
- DELETE
190+
- HEAD
191+
MaxAge: 3000
177192

178193
# TODO: Complete the policy below and attach it to the bucket defined above
179194
# For cross referencing the name of the bucket, use "!Ref AttachmentsBucket"
180-
# BucketPolicy:
181-
# Type: AWS::S3::BucketPolicy
182-
# Properties:
195+
BucketPolicy:
196+
Type: AWS::S3::BucketPolicy
197+
Properties:
198+
PolicyDocument:
199+
Id: MyPolicy
200+
Version: "2012-10-17"
201+
Statement:
202+
- Sid: PublicReadForGetBucketObjects
203+
Effect: Allow
204+
Principal: '*'
205+
Action: 's3:*'
206+
Resource: 'arn:aws:s3:::${self:provider.environment.ATTACHMENT_S3_BUCKET}/*'
207+
Bucket: !Ref AttachmentsBucket

course-04/project/c4-final-project-starter-code/backend/src/helpers/todos.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import { TodoAccess } from './todosAcess'
2-
// import { AttachmentUtils } from './attachmentUtils';
32
import { TodoItem } from '../models/TodoItem'
43
import { CreateTodoRequest } from '../requests/CreateTodoRequest'
5-
// import { UpdateTodoRequest } from '../requests/UpdateTodoRequest'
6-
// import { createLogger } from '../utils/logger'
74
import * as uuid from 'uuid'
85
import { getUserId } from '../lambda/utils'
96
import { APIGatewayProxyEvent } from 'aws-lambda'
10-
7+
import { UpdateTodoRequest } from '../requests/UpdateTodoRequest'
118

129
const todoAccess = new TodoAccess()
1310

14-
export async function getTodosForUser(){
15-
const todos = await getTodosForUser()
11+
export async function getTodosForUser(userId: string){
12+
const todos = await todoAccess.getTodosforUser(userId)
1613
return todos
1714
}
1815

@@ -29,9 +26,61 @@ export async function createTodo(
2926
createdAt: new Date().toISOString(),
3027
name: createTodoRequest.name,
3128
dueDate: createTodoRequest.dueDate,
32-
done: false
29+
done: false,
30+
attachmentUrl: ''
3331
}
34-
3532
return await todoAccess.createTodo(todo)
3633
}
3734

35+
export async function updateTodo(
36+
updateTodoRequest: UpdateTodoRequest,
37+
todoId: string,
38+
event: APIGatewayProxyEvent
39+
): Promise<TodoItem> {
40+
41+
const itemId = todoId
42+
const userId = getUserId(event)
43+
const todo = {
44+
userId: userId,
45+
todoId: itemId,
46+
createdAt: new Date().toISOString(),
47+
name: updateTodoRequest.name,
48+
dueDate: updateTodoRequest.dueDate,
49+
done: updateTodoRequest.done
50+
}
51+
return await todoAccess.updateTodo(todo)
52+
}
53+
54+
export async function deleteTodo(
55+
todoId: string,
56+
event: APIGatewayProxyEvent
57+
): Promise<string> {
58+
const userId = getUserId(event)
59+
return await todoAccess.deleteTodo(todoId, userId)
60+
}
61+
62+
export async function createAttachmentPresignedUrl(
63+
todoId: string
64+
): Promise<string> {
65+
return todoAccess.getUploadUrl(todoId)
66+
}
67+
68+
export async function updateUrl(
69+
updateTodoRequest: UpdateTodoRequest,
70+
todoId: string,
71+
event: APIGatewayProxyEvent
72+
): Promise<TodoItem> {
73+
74+
const itemId = todoId
75+
const userId = getUserId(event)
76+
const todo = {
77+
userId: userId,
78+
todoId: itemId,
79+
createdAt: new Date().toISOString(),
80+
name: updateTodoRequest.name,
81+
dueDate: updateTodoRequest.dueDate,
82+
done: updateTodoRequest.done
83+
}
84+
return await todoAccess.updateUrl(todo)
85+
}
86+

course-04/project/c4-final-project-starter-code/backend/src/helpers/todosAcess.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import * as AWS from 'aws-sdk'
22
import * as AWSXRay from 'aws-xray-sdk'
33
import { DocumentClient } from 'aws-sdk/clients/dynamodb'
4-
//import { createLogger } from '../utils/logger'
54
import { TodoItem } from '../models/TodoItem'
6-
//import { TodoUpdate } from '../models/TodoUpdate';
75

86
const XAWS = AWSXRay.captureAWS(AWS)
9-
const s3 = XAWS.S3({
7+
const s3 = new XAWS.S3({
108
signatureVersion: 'v4'
119
})
1210
//const logger = createLogger('TodosAccess')
@@ -15,15 +13,19 @@ export class TodoAccess {
1513
constructor(
1614
private readonly docClient: DocumentClient = createDynamoDBClient(),
1715
private readonly todosTable = process.env.TODOS_TABLE,
18-
private readonly bucketName = process.env.IMAGES_S3_BUCKET,
16+
private readonly bucketName = process.env.ATTACHMENT_S3_BUCKET,
1917
private readonly urlExpiration = process.env.SIGNED_URL_EXPIRATION
2018
) {}
2119

22-
async getTodosforUser(){
23-
const todos = await this.docClient.scan({
24-
TableName: this.todosTable
20+
async getTodosforUser(userId: string){
21+
const todos = await this.docClient.query({
22+
TableName: this.todosTable,
23+
KeyConditionExpression: 'userId = :userId',
24+
ExpressionAttributeValues: {
25+
':userId': userId
26+
}
2527
}).promise()
26-
return todos
28+
return todos.Items
2729
}
2830

2931

@@ -37,13 +39,52 @@ export class TodoAccess {
3739
return todo
3840
}
3941

40-
async getUploadUrl(imageId: string) {
42+
async updateTodo(todo: TodoItem): Promise<TodoItem> {
43+
await this.docClient.update({
44+
TableName: this.todosTable,
45+
Key: {
46+
userId: todo.userId,
47+
todoId: todo.todoId
48+
},
49+
UpdateExpression: 'set done = :s',
50+
ExpressionAttributeValues: {':s': todo.done}
51+
}).promise()
52+
return todo
53+
}
54+
55+
56+
async deleteTodo(todoId: string, userId: string): Promise<string> {
57+
await this.docClient.delete({
58+
TableName: this.todosTable,
59+
Key: {
60+
todoId: todoId,
61+
userId: userId
62+
}
63+
}).promise()
64+
return todoId
65+
}
66+
67+
async getUploadUrl(imageId: string){
4168
return s3.getSignedUrl('putObject', {
4269
Bucket: this.bucketName,
4370
Key: imageId,
4471
Expires: this.urlExpiration
4572
})
4673
}
74+
75+
async updateUrl(todo: TodoItem): Promise<TodoItem> {
76+
const attachmentUrl = `https://${this.bucketName}.s3.amazonaws.com/${todo.todoId}`
77+
await this.docClient.update({
78+
TableName: this.todosTable,
79+
Key: {
80+
userId: todo.userId,
81+
todoId: todo.todoId
82+
},
83+
UpdateExpression: 'set attachmentUrl = :s',
84+
ExpressionAttributeValues: {':s': attachmentUrl}
85+
}).promise()
86+
return todo
87+
}
4788
}
4889

4990
function createDynamoDBClient() {
Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
1-
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'
2-
import 'source-map-support/register'
3-
import * as middy from 'middy'
4-
import { cors } from 'middy/middlewares'
1+
import { APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyResult } from 'aws-lambda'
52
import { CreateTodoRequest } from '../../requests/CreateTodoRequest'
6-
//import { getUserId } from '../utils';
73
import { createTodo } from '../../helpers/todos'
84

9-
export const handler = middy(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
5+
export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
106
const newTodo: CreateTodoRequest = JSON.parse(event.body)
11-
12-
137
const newItem = await createTodo(newTodo, event)
148

159
return {
1610
statusCode: 201,
11+
headers: {
12+
'Access-Control-Allow-Origin': '*',
13+
'Access-Control-Allow-Credentials': true
14+
},
1715
body: JSON.stringify({
18-
newItem
16+
item: newItem
1917
})
2018
}
2119
}
22-
)
2320

24-
handler.use(
25-
cors({
26-
credentials: true
27-
})
28-
)
Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
1-
// import 'source-map-support/register'
1+
import { APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyResult } from 'aws-lambda'
2+
import { deleteTodo } from '../../helpers/todos'
23

3-
// import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'
4-
// import * as middy from 'middy'
5-
// import { cors, httpErrorHandler } from 'middy/middlewares'
4+
export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
5+
const todoId = event.pathParameters.todoId
6+
const newItem = await deleteTodo(todoId, event)
7+
console.log('Todo Updated', newItem)
8+
return {
9+
statusCode: 200,
10+
headers: {
11+
'Access-Control-Allow-Origin': '*',
12+
'Access-Control-Allow-Credentials': true
13+
},
14+
body: JSON.stringify({})
15+
}
16+
}
617

7-
// //import { deleteTodo } from '../../businessLogic/todos'
8-
// //import { getUserId } from '../utils'
918

10-
// export const handler = middy(
11-
// async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
12-
// const todoId = event.pathParameters.todoId
13-
// console.log(todoId)
14-
// // TODO: Remove a TODO item by id
15-
16-
// return undefined
17-
// }
18-
// )
19-
20-
// handler
21-
// .use(httpErrorHandler())
22-
// .use(
23-
// cors({
24-
// credentials: true
25-
// })
26-
// )
Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
1-
import 'source-map-support/register'
1+
import { APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyResult } from 'aws-lambda'
2+
import { createAttachmentPresignedUrl, updateUrl } from '../../helpers/todos'
3+
import { UpdateTodoRequest } from '../../requests/UpdateTodoRequest'
24

3-
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'
4-
import * as middy from 'middy'
5-
import { cors, httpErrorHandler } from 'middy/middlewares'
6-
7-
//import { createAttachmentPresignedUrl } from '../../businessLogic/todos'
8-
//import { getUserId } from '../utils'
9-
10-
export const handler = middy(
11-
async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
12-
const todoId = event.pathParameters.todoId
13-
console.log(todoId)
14-
15-
// TODO: Return a presigned URL to upload a file for a TODO item with the provided id
16-
17-
18-
return undefined
5+
export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
6+
const updatedTodo: UpdateTodoRequest = JSON.parse(event.body)
7+
const todoId = event.pathParameters.todoId
8+
const signedUrl = await createAttachmentPresignedUrl(todoId)
9+
await updateUrl(updatedTodo, todoId, event)
10+
return {
11+
statusCode: 200,
12+
headers: {
13+
'Access-Control-Allow-Origin': '*',
14+
'Access-Control-Allow-Credentials': true
15+
},
16+
body: JSON.stringify({uploadUrl: signedUrl})
1917
}
20-
)
21-
22-
handler
23-
.use(httpErrorHandler())
24-
.use(
25-
cors({
26-
credentials: true
27-
})
28-
)
18+
}

0 commit comments

Comments
 (0)