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

prepare app to work with community version of LocalStack #4

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ jobs:

- name: Start LocalStack
env:
LOCALSTACK_API_KEY: ${{ secrets.LOCALSTACK_API_KEY }}
DNS_ADDRESS: 0
run: |
pip install localstack awscli-local[ver1]
pip install terraform-local
docker pull localstack/localstack-pro:latest
docker pull localstack/localstack:latest
# Start LocalStack in the background
DEBUG=1 localstack start -d
# Wait 30 seconds for the LocalStack container to become ready before timing out
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ We are using the following AWS services and their features to build our infrastr
Start LocalStack Pro with the `LOCALSTACK_API_KEY` pre-configured:

```shell
export LOCALSTACK_API_KEY=<your-api-key>
EXTRA_CORS_ALLOWED_ORIGINS=* localstack start
```

Expand Down Expand Up @@ -162,5 +161,4 @@ The sample application is based on a public [AWS sample app](https://github.com/
## Contributing

We appreciate your interest in contributing to our project and are always looking for new ways to improve the developer experience. We welcome feedback, bug reports, and even feature ideas from the community.
Please refer to the [contributing file](CONTRIBUTING.md) for more details on how to get started.

Please refer to the [contributing file](CONTRIBUTING.md) for more details on how to get started.
14 changes: 13 additions & 1 deletion packages/backend/src/createNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,19 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
let client: DynamoDBClient;

if (process.env.LOCALSTACK_HOSTNAME) {
const localStackConfig = {
endpoint: `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT}`,
region: "us-east-1", // Change the region as per your setup
};
client = new DynamoDBClient(localStackConfig);
} else {
// Use the default AWS configuration
client = new DynamoDBClient({});
}

await client.send(new PutItemCommand(params));
return success(params.Item);
} catch (e) {
Expand Down
13 changes: 12 additions & 1 deletion packages/backend/src/deleteNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,18 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
let client: DynamoDBClient;

if (process.env.LOCALSTACK_HOSTNAME) {
const localStackConfig = {
endpoint: `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT}`,
region: "us-east-1", // Change the region as per your setup
};
client = new DynamoDBClient(localStackConfig);
} else {
// Use the default AWS configuration
client = new DynamoDBClient({});
}
await client.send(new DeleteItemCommand(params));
return success({ status: true });
} catch (e) {
Expand Down
17 changes: 14 additions & 3 deletions packages/backend/src/getNote.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
import { success, failure } from "./libs/response";
import { success, failure, not_found } from "./libs/response";

// eslint-disable-next-line no-unused-vars
import { APIGatewayEvent } from "aws-lambda";
Expand All @@ -14,13 +14,24 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
let client: DynamoDBClient;

if (process.env.LOCALSTACK_HOSTNAME) {
const localStackConfig = {
endpoint: `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT}`,
region: "us-east-1", // Change the region as per your setup
};
client = new DynamoDBClient(localStackConfig);
} else {
// Use the default AWS configuration
client = new DynamoDBClient({});
}
const result = await client.send(new GetItemCommand(params));
if (result.Item) {
// Return the retrieved item
return success(unmarshall(result.Item));
} else {
return failure({ status: false, error: "Item not found." });
return not_found({ status: false, error: "Item not found." });
}
} catch (e) {
console.log(e);
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/libs/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export const failure = (body: any) => {
return buildResponse(500, body);
};

export const not_found = (body: any) => {
return buildResponse(404, body);
};

const buildResponse = (statusCode: number, body: any) => ({
statusCode: statusCode,
headers: {
Expand Down
13 changes: 12 additions & 1 deletion packages/backend/src/listNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@ export const handler = async () => {
};

try {
const client = new DynamoDBClient({});
let client: DynamoDBClient;

if (process.env.LOCALSTACK_HOSTNAME) {
const localStackConfig = {
endpoint: `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT}`,
region: "us-east-1", // Change the region as per your setup
};
client = new DynamoDBClient(localStackConfig);
} else {
// Use the default AWS configuration
client = new DynamoDBClient({});
}
const result = await client.send(new ScanCommand(params));
// Return the matching list of items in response body
return success(result.Items.map((Item) => unmarshall(Item)));
Expand Down
13 changes: 12 additions & 1 deletion packages/backend/src/updateNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
let client: DynamoDBClient;

if (process.env.LOCALSTACK_HOSTNAME) {
const localStackConfig = {
endpoint: `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT}`,
region: "us-east-1", // Change the region as per your setup
};
client = new DynamoDBClient(localStackConfig);
} else {
// Use the default AWS configuration
client = new DynamoDBClient({});
}
await client.send(new UpdateItemCommand(params));
return success({ status: true });
} catch (e) {
Expand Down
3 changes: 0 additions & 3 deletions packages/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@ Ensure that you've followed pre-requisites from main [README](../../README.md),

- `yarn prepare:frontend` to populate Cloudformation resources in frontend config.
- The resources can also be manually added in [`src/config.json`](./src/config.json).
- Add `aws-js-sdk-notes-app.FilesBucket` from CDK output for `FILES_BUCKET`.
- Add `aws-js-sdk-notes-app.GatewayUrl` from CDK output for `GATEWAY_URL`.
- Example GatewayURL: `https://randomstring.execute-api.region.amazonaws.com/prod/`
- Add `aws-js-sdk-notes-app.IdentityPoolId` from CDK output for `IDENTITY_POOL_ID`.
- Example IdentityPoolId: `region:random-strc-4ce1-84ee-9a429f9b557e`
- Add `aws-js-sdk-notes-app.Region` from CDK output for `REGION`.
- `yarn start:frontend` to run the server.
- This will open the website in the browser, and enable HMR.
Expand Down
2 changes: 0 additions & 2 deletions packages/frontend/src/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
{
"FILES_BUCKET": "<copy-output-FilesBucket-from-cdk-deploy>",
"GATEWAY_URL": "<copy-output-GatewayUrl-from-cdk-deploy>",
"IDENTITY_POOL_ID": "<copy-output-IdentityPoolId-from-cdk-deploy>",
"REGION": "<copy-output-Region-from-cdk-deploy>",
"MAX_FILE_SIZE": 2000000
}
4 changes: 1 addition & 3 deletions packages/frontend/src/content/CreateNote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState, FormEvent } from "react";
import { Form, Button, Alert } from "react-bootstrap";
import { navigate, RouteComponentProps } from "@reach/router";
import { GATEWAY_URL, MAX_FILE_SIZE } from "../config.json";
import { putObject } from "../libs";
import { HomeButton, ButtonSpinner, PageContainer } from "../components";

const CreateNote = (props: RouteComponentProps) => {
Expand All @@ -26,10 +25,9 @@ const CreateNote = (props: RouteComponentProps) => {

try {
// @ts-ignore Argument of type 'undefined' is not assignable to parameter of type 'File'
const attachment = file ? await putObject(file) : undefined;
await fetch(createNoteURL, {
method: "POST",
body: JSON.stringify({ attachment, content: noteContent }),
body: JSON.stringify({ content: noteContent }),
});
navigate("/");
} catch (error) {
Expand Down
4 changes: 0 additions & 4 deletions packages/frontend/src/content/DeleteNoteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState } from "react";
import { Button, Alert } from "react-bootstrap";
import { GATEWAY_URL } from "../config.json";
import { navigate } from "@reach/router";
import { deleteObject } from "../libs";
import { ButtonSpinner } from "../components";

const DeleteNoteButton = (props: { noteId: string; attachment?: string }) => {
Expand All @@ -17,9 +16,6 @@ const DeleteNoteButton = (props: { noteId: string; attachment?: string }) => {
const deleteNoteURL = `${GATEWAY_URL}notes/${noteId}`;

try {
if (attachment) {
await deleteObject(attachment);
}
await fetch(deleteNoteURL, {
method: "DELETE",
});
Expand Down
5 changes: 0 additions & 5 deletions packages/frontend/src/content/ShowNote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { RouteComponentProps, navigate } from "@reach/router";
import { Form, Card } from "react-bootstrap";
import { GATEWAY_URL } from "../config.json";
import { DeleteNoteButton, SaveNoteButton } from "./";
import { getObjectUrl } from "../libs";
import { HomeButton, Loading, PageContainer } from "../components";

const ShowNote = (props: RouteComponentProps<{ noteId: string }>) => {
Expand All @@ -22,10 +21,6 @@ const ShowNote = (props: RouteComponentProps<{ noteId: string }>) => {
const response = await fetch(fetchURL);
const data = await response.json();
setNoteContent(data.content);
if (data.attachment) {
setAttachment(data.attachment);
setAttachmentURL(await getObjectUrl(data.attachment));
}
} catch (error) {
// Navigate to 404 page, as noteId probably not present
navigate("/404");
Expand Down
13 changes: 0 additions & 13 deletions packages/frontend/src/libs/deleteObject.ts

This file was deleted.

26 changes: 0 additions & 26 deletions packages/frontend/src/libs/getObjectUrl.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/frontend/src/libs/index.ts

This file was deleted.

17 changes: 0 additions & 17 deletions packages/frontend/src/libs/putObject.ts

This file was deleted.

14 changes: 0 additions & 14 deletions packages/frontend/src/libs/s3Client.ts

This file was deleted.

57 changes: 0 additions & 57 deletions packages/infra/cdk/aws-sdk-js-notes-app-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,64 +73,7 @@ export class AwsSdkJsNotesAppStack extends Stack {
)
);

const filesBucket = new s3.Bucket(this, "files-bucket");
filesBucket.addCorsRule({
allowedOrigins: apigw.Cors.ALL_ORIGINS, // NOT recommended for production code
allowedMethods: [
s3.HttpMethods.PUT,
s3.HttpMethods.GET,
s3.HttpMethods.DELETE,
],
allowedHeaders: ["*"],
});

const identityPool = new cognito.CfnIdentityPool(this, "identity-pool", {
allowUnauthenticatedIdentities: true,
});

const unauthenticated = new iam.Role(this, "unauthenticated-role", {
assumedBy: new iam.FederatedPrincipal(
"cognito-identity.amazonaws.com",
{
StringEquals: {
"cognito-identity.amazonaws.com:aud": identityPool.ref,
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "unauthenticated",
},
},
"sts:AssumeRoleWithWebIdentity"
),
});

// NOT recommended for production code - only give read permissions for unauthenticated resources
filesBucket.grantRead(unauthenticated);
filesBucket.grantPut(unauthenticated);
filesBucket.grantDelete(unauthenticated);

// Add policy to start Transcribe stream transcription
unauthenticated.addToPolicy(
new iam.PolicyStatement({
resources: ["*"],
actions: ["transcribe:StartStreamTranscriptionWebSocket"],
})
);

// Add policy to enable Amazon Polly text-to-speech
unauthenticated.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonPollyFullAccess")
);

new cognito.CfnIdentityPoolRoleAttachment(this, "role-attachment", {
identityPoolId: identityPool.ref,
roles: {
unauthenticated: unauthenticated.roleArn,
},
});

new CfnOutput(this, "FilesBucket", { value: filesBucket.bucketName });
new CfnOutput(this, "GatewayUrl", { value: api.url });
new CfnOutput(this, "IdentityPoolId", { value: identityPool.ref });
new CfnOutput(this, "Region", { value: this.region });
}
}
Loading