Skip to content

Commit

Permalink
Merge branch 'nextjs' into feature-1137-terraform-ecs
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewpeng02 authored Mar 1, 2024
2 parents a211b97 + 453d6e9 commit af15f44
Show file tree
Hide file tree
Showing 14 changed files with 2,539 additions and 1,320 deletions.
15 changes: 10 additions & 5 deletions serverless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,28 @@
"removeprod": "sst remove --stage prod",
"console": "sst console",
"typecheck": "tsc --noEmit",
"lint": "npx eslint ."
"lint": "npx eslint .",
"test": "sst bind vitest"
},
"devDependencies": {
"@tsconfig/node16": "^16.1.1",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"aws-cdk-lib": "2.91.0",
"constructs": "10.2.69",
"aws-cdk-lib": "2.124.0",
"aws-sdk-client-mock": "^3.0.1",
"aws-sdk-client-mock-vitest": "^1.0.0",
"constructs": "10.3.0",
"eslint": "^8.53.0",
"sst": "^2.32.2",
"typescript": "^5.2.2"
"sst": "2.40.1",
"typescript": "^5.2.2",
"vitest": "^0.34.6"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.418.0",
"@aws-sdk/lib-dynamodb": "^3.418.0",
"@aws-sdk/s3-request-presigner": "^3.391.0",
"@aws-sdk/types": "^3.418.0",
"@aws-sdk/util-dynamodb": "^3.515.0",
"@types/uuid": "^9.0.4"
}
}
2 changes: 1 addition & 1 deletion serverless/packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"devDependencies": {
"@types/node": "^20.5.0",
"sst": "^2.24.3",
"sst": "2.40.1",
"vitest": "^0.34.1"
},
"dependencies": {}
Expand Down
2 changes: 1 addition & 1 deletion serverless/packages/core/src/parseJwt.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default function parseJwt(token: string) {
return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
}
}
2 changes: 1 addition & 1 deletion serverless/packages/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
"dependencies": {
"uuid": "^9.0.1"
}
}
}
61 changes: 61 additions & 0 deletions serverless/packages/functions/src/user/create_user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from "aws-lambda";
import parseJwt from "../../../core/src/parseJwt";
import { v4 as uuidv4 } from 'uuid';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand, PutCommandInput } from '@aws-sdk/lib-dynamodb';

export async function handler<APIGatewayProxyHandlerV2>(event : APIGatewayProxyEventV2) {
if (event) {
const user_id: string = parseJwt(event.headers.authorization ?? "")["user_id"];
const eventBody = JSON.parse(event.body? event.body : "");
let putCommandInput: PutCommandInput = {
TableName: "UserTable",
Item:
{
user_id: user_id,
name: eventBody['name'],
email: eventBody['email'],
phone: eventBody['phone']
}
}

if (putCommandInput == null)
{
return {
statusCode: 400,
body: JSON.stringify({ message: "Invalid request body" })
}
}

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

const command = new PutCommand(putCommandInput);
const response = await docClient.send(command);

if (response.$metadata.httpStatusCode != 200) {
return {
statusCode: 500,
body: JSON.stringify({ message: "Internal server error."})
};
}

return {
statusCode: 200,
body: JSON.stringify({ user_id: user_id, message: "Successfully created a new user."})
};
}
return {
statusCode: 404,
body: JSON.stringify({ message: "Not Found" }),
};
};
function removeUndefinedValues(obj: { [key: string]: any }) {
const newObj: { [key: string]: any } = {};
for (const key in obj) {
if (obj[key] !== undefined) {
newObj[key] = obj[key];
}
}
return newObj;
}
39 changes: 39 additions & 0 deletions serverless/packages/functions/src/user/delete_user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from "aws-lambda";
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, DeleteCommand } from '@aws-sdk/lib-dynamodb';
import parseJwt from "../../../core/src/parseJwt";

export async function handler<APIGatewayProxyHandlerV2>(event : APIGatewayProxyEventV2) {
if (event) {
const user_id: string = parseJwt(event.headers.authorization ?? "")["user_id"];

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

const command = new DeleteCommand({
TableName : "UserTable",
Key :
{
user_id: user_id
}
});

const response = await docClient.send(command);

if (response.$metadata.httpStatusCode == undefined || response.$metadata.httpStatusCode != 200)
{
return {
statusCode: 404,
body: JSON.stringify({ message : "Delete operation failed" })
}
}
return {
statusCode: 200,
body: "Successfully deleted user with id " + user_id
}
}
return {
statusCode: 400,
body: JSON.stringify({ message : "Malformed request content" }),
};
};
39 changes: 39 additions & 0 deletions serverless/packages/functions/src/user/get_user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from "aws-lambda";
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb';
import parseJwt from "../../../core/src/parseJwt";

export async function handler<APIGatewayProxyHandlerV2>(event : APIGatewayProxyEventV2) {
if (event)
{
const user_id: string = parseJwt(event.headers.authorization ?? "")["user_id"];
const client: DynamoDBClient = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

const command : GetCommand = new GetCommand({
TableName : "UserTable",
Key :
{
user_id : user_id
}
});

const response = await docClient.send(command);

if (!response.Item)
{
return {
statusCode: 404,
body: JSON.stringify({message: "Provided User ID does not exist"})
}
}
return {
statusCode: 200,
body: JSON.stringify({message: "Successfully retrieved User data", user: response.Item})
}
}
return {
statusCode: 400,
body: JSON.stringify({message: "Malformed request content"})
};
}
78 changes: 78 additions & 0 deletions serverless/packages/functions/src/user/tests/create_user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from "aws-lambda";
import { beforeEach, expect, it, vi} from "vitest";
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DeleteCommand, DynamoDBDocumentClient, GetCommand, PutCommand } from '@aws-sdk/lib-dynamodb';
import {mockClient} from 'aws-sdk-client-mock';
import { handler } from '../create_user';
import 'aws-sdk-client-mock-vitest';


//mocks parseJwt so that the call just returns whatever the input is
vi.mock('../../../../core/src/parseJwt', async () => {
return {
default: vi.fn().mockImplementation(input => input),
}
})

beforeEach(async () => {
ddbMock.reset();
})

const ddbMock = mockClient(DynamoDBDocumentClient);

const dynamodb = new DynamoDBClient({});
const ddb = DynamoDBDocumentClient.from(dynamodb);

it("test successful create user call", async () => {
ddbMock.on(PutCommand).resolves({
$metadata: {
httpStatusCode: 200,
}
})
//error is fine, doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason
const event: APIGatewayProxyEventV2 = {
headers: {
authorization: 'abcd',
},
body: '{\n' +
' "name": "SETH SHI BUT UPDATED",\n' +
' "email": "[email protected]",\n' +
' "phone": "123-456-7890"\n' +
'}',
}

const result = await handler(event);
expect(result.statusCode).toEqual(200);
});

it("test internal service error", async () => {
ddbMock.on(PutCommand).resolves({
$metadata: {
httpStatusCode: 456,
}
})
//error is fine, doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason
const event: APIGatewayProxyEventV2 = {
headers: {
authorization: 'abcd',
},
body: '{\n' +
' "name": "SETH SHI BUT UPDATED",\n' +
' "email": "[email protected]",\n' +
' "phone": "123-456-7890"\n' +
'}',
}

const result = await handler(event);
expect(result.statusCode).toEqual(500);
});

it("test undefined event", async () => {
ddbMock.on(PutCommand).resolves({
$metadata: {
httpStatusCode: 400,
}
})
const result = await handler(undefined);
expect(result.statusCode).toEqual(404);
});
95 changes: 95 additions & 0 deletions serverless/packages/functions/src/user/tests/delete_user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from "aws-lambda";
import { beforeEach, expect, it, vi} from "vitest";
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DeleteCommand, DynamoDBDocumentClient, GetCommand, PutCommand } from '@aws-sdk/lib-dynamodb';
import {mockClient} from 'aws-sdk-client-mock';
import { handler } from '../delete_user';
import 'aws-sdk-client-mock-vitest';


//mocks parseJwt so that the call just returns whatever the input is
vi.mock('../../../../core/src/parseJwt', async () => {
return {
default: vi.fn().mockImplementation(input => input),
}
})

beforeEach(async () => {
ddbMock.reset();
})

const ddbMock = mockClient(DynamoDBDocumentClient);

const dynamodb = new DynamoDBClient({});
const ddb = DynamoDBDocumentClient.from(dynamodb);

it("test successful delete user call", async () => {
ddbMock.on(DeleteCommand).resolves({
$metadata: {
httpStatusCode: 200,
}
})
//error is fine, doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason
const event: APIGatewayProxyEventV2 = {
headers: {
authorization: 'abcd',
},
body: '{\n' +
' "name": "SETH SHI BUT UPDATED",\n' +
' "email": "[email protected]",\n' +
' "phone": "123-456-7890"\n' +
'}',
}

const result = await handler(event);
expect(result.statusCode).toEqual(200);
});

it("test no response failed operation call", async () => {
ddbMock.on(DeleteCommand).resolves({
$metadata: {
httpStatusCode: undefined,
}
})

const event: APIGatewayProxyEventV2 = {
headers: {
authorization: 'abcd',
},
body: '{\n' +
' "name": "SETH SHI BUT UPDATED",\n' +
' "email": "[email protected]",\n' +
' "phone": "123-456-7890"\n' +
'}',
}

const result = await handler(event);
expect(result.statusCode).toEqual(404);
});

it("test different status code failed operation call", async () => {
ddbMock.on(DeleteCommand).resolves({
$metadata: {
httpStatusCode: 267,
}
})
//error is fine, doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason
const event: APIGatewayProxyEventV2 = {
headers: {
authorization: 'abcd',
},
body: '{\n' +
' "name": "SETH SHI BUT UPDATED",\n' +
' "email": "[email protected]",\n' +
' "phone": "123-456-7890"\n' +
'}',
}

const result = await handler(event);
expect(result.statusCode).toEqual(404);
});

it("test malformed call", async () => {
const result = await handler(undefined);
expect(result.statusCode).toEqual(400);
});
Loading

0 comments on commit af15f44

Please sign in to comment.