Skip to content

Instantly share code, notes, and snippets.

@tlakomy
Last active March 4, 2021 01:26
Show Gist options
  • Select an option

  • Save tlakomy/f1312ec1fd092ece75a0f72403235fc8 to your computer and use it in GitHub Desktop.

Select an option

Save tlakomy/f1312ec1fd092ece75a0f72403235fc8 to your computer and use it in GitHub Desktop.

Revisions

  1. Tomasz Łakomy revised this gist Apr 6, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -678,7 +678,7 @@ headers: {

    ```
    new s3Deployment.BucketDeployment(this, "DeployWebsite", {
    sources: [s3Deployment.Source.asset("../../frontend/build")],
    sources: [s3Deployment.Source.asset("../frontend/build")],
    destinationBucket: websiteBucket
    });
    ```
  2. Tomasz Łakomy revised this gist Apr 6, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -678,7 +678,7 @@ headers: {

    ```
    new s3Deployment.BucketDeployment(this, "DeployWebsite", {
    sources: [s3Deployment.Source.asset("../frontend/build")],
    sources: [s3Deployment.Source.asset("../../frontend/build")],
    destinationBucket: websiteBucket
    });
    ```
  3. Tomasz Łakomy revised this gist Apr 5, 2020. 1 changed file with 17 additions and 0 deletions.
    17 changes: 17 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -486,6 +486,23 @@ const addTodoItem = async (data: { todo: string; id: string }) => {
    }
    return todo;
    };
    const deleteTodoItem = async (data: { id: string }) => {
    const { id } = data;
    if (id && id !== "") {
    await dynamo
    .delete({
    TableName: tableName,
    Key: {
    id
    }
    })
    .promise();
    }
    return id;
    };
    ```

    - Go to the lambda directory and run `npm init` to create a `package.json` and install `uuid` package
  4. Tomasz Łakomy revised this gist Apr 5, 2020. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -328,6 +328,21 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    };
    ```

    - Implement the `getAllTodos` function:

    ```js
    const getAllTodos = async () => {
    const scanResult = await dynamo
    .scan({
    TableName: tableName
    })
    .promise();

    return scanResult;
    };
    ```


    - Deploy the `todoHandler.ts` function in the `TodoDatabase` stack

    ```
  5. Tomasz Łakomy revised this gist Mar 21, 2020. 1 changed file with 47 additions and 6 deletions.
    53 changes: 47 additions & 6 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -445,16 +445,57 @@ and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda funct

    - Refactor the `todoHandler` function so it can handle both `GET` and `POST` requests, along with some error handling (notice that currently the id is **HARDCODED**):

    **REWRITE THIS SECTION**
    ```
    const getAllTodos = async () => {
    const scanResult = await dynamo
    .scan({
    TableName: tableName
    })
    .promise();
    - Go to the lambda directory and run `npm init` to create a `package.json` and install `uuid` package
    - Modify the `POST` section of the lambda function to use the `uuid` package:
    return scanResult;
    };
    **REWRITE THIS SECTION**
    const addTodoItem = async (data: { todo: string; id: string }) => {
    const { id, todo } = data;
    if (todo && todo !== "") {
    await dynamo
    .put({
    TableName: tableName,
    Item: {
    id: "123",
    todo
    }
    })
    .promise();
    }
    return todo;
    };
    ```

    - Add a `DELETE` handler so we'll be able to delete a todo item:
    - Go to the lambda directory and run `npm init` to create a `package.json` and install `uuid` package
    - Modify the `POST` section of the lambda function to use the `uuid` package:

    **REWRITE THIS SECTION**
    ```
    const uuid = require("uuid/v4");
    ...
    ...
    const addTodoItem = async (data: { todo: string; id: string }) => {
    const { id, todo } = data;
    if (todo && todo !== "") {
    await dynamo
    .put({
    TableName: tableName,
    Item: {
    id: id || uuid(),
    todo
    }
    })
    .promise();
    }
    return todo;
    };
    ```

    - At the end, our lambda function should look similar to this:

  6. Tomasz Łakomy revised this gist Mar 21, 2020. 1 changed file with 32 additions and 30 deletions.
    62 changes: 32 additions & 30 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -459,42 +459,42 @@ and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda funct
    - At the end, our lambda function should look similar to this:

    ```
    const { DynamoDB } = require("aws-sdk");
    const uuid = require("uuid");
    /// <reference types="aws-sdk" />
    import AWS = require("aws-sdk");
    const uuid = require("uuid/v4");
    const tableName = process.env.TABLE_NAME || "";
    const dynamo = new AWS.DynamoDB.DocumentClient();
    const createResponse = (body: string, statusCode = 200) => {
    const createResponse = (
    body: string | AWS.DynamoDB.DocumentClient.ItemList,
    statusCode = 200
    ) => {
    return {
    statusCode,
    headers: {
    "Content-Type": "text/plain",
    "Access-Control-Allow-Origin": "*" // Required for CORS support to work
    },
    body: JSON.stringify(body, null, 2)
    };
    };
    const tableName = process.env.TABLE_NAME;
    const dynamo = new DynamoDB();
    const getAlllTodos = async () => {
    const getAllTodos = async () => {
    const scanResult = await dynamo
    .scan({
    "TableName": tableName,
    "Limit": 10
    TableName: tableName
    })
    .promise();
    return scanResult.Items.map(DynamoDB.Converter.unmarshall);
    return scanResult;
    };
    const addTodoItem = async (data: { todo: string; id: string }) => {
    const { id, todo } = data;
    if (todo && todo !== "") {
    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: id || uuid() },
    todo: { S: todo }
    .put({
    TableName: tableName,
    Item: {
    id: id || uuid(),
    todo
    }
    })
    .promise();
    @@ -504,12 +504,13 @@ const addTodoItem = async (data: { todo: string; id: string }) => {
    const deleteTodoItem = async (data: { id: string }) => {
    const { id } = data;
    if (id && id !== "") {
    await dynamo
    .deleteItem({
    "TableName": tableName,
    "Key": {
    id: { S: id }
    .delete({
    TableName: tableName,
    Key: {
    id
    }
    })
    .promise();
    @@ -523,13 +524,13 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    const { httpMethod, body: requestBody } = event;
    if (httpMethod === "GET") {
    const response = await getAlllTodos();
    const response = await getAllTodos();
    return createResponse(response);
    return createResponse(response.Items || []);
    }
    if (!requestBody) {
    return createResponse("Missing request body", 400);
    return createResponse("Missing request body", 500);
    }
    const data = JSON.parse(requestBody);
    @@ -538,7 +539,7 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    const todo = await addTodoItem(data);
    return todo
    ? createResponse(`${todo} added to the database`)
    : createResponse("Todo is missing", 400);
    : createResponse("Todo is missing", 500);
    }
    if (httpMethod === "DELETE") {
    @@ -547,15 +548,16 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    ? createResponse(
    `Todo item with an id of ${id} deleted from the database`
    )
    : createResponse("ID is missing", 400);
    : createResponse("ID is missing", 500);
    }
    return createResponse(
    `We only accept GET, POST, and DELETE, not ${httpMethod}`,
    400
    `We only accept GET, POST, OPTIONS and DELETE, not ${httpMethod}`,
    500
    );
    } catch (error) {
    return createResponse(error, 400);
    console.log(error);
    return createResponse(error, 500);
    }
    };
    ```
  7. Tomasz Łakomy revised this gist Mar 21, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -344,7 +344,7 @@ export class TodoDatabase extends cdk.Construct {
    });
    new lambda.Function(this, "TodoHandler", {
    code: lambda.Code.fromAsset("lambda"),
    code: lambda.Code.fromAsset("lesson_07/lambda"),
    handler: "todoHandler.handler",
    runtime: lambda.Runtime.NODEJS_12_X,
    environment: {
    @@ -373,7 +373,7 @@ export class TodoDatabase extends cdk.Construct {
    });
    this.handler = new lambda.Function(this, "TodoHandler", {
    code: lambda.Code.fromAsset("lambda"),
    code: lambda.Code.fromAsset("lesson_07/lambda"),
    handler: "todoHandler.handler",
    runtime: lambda.Runtime.NODEJS_12_X,
    environment: {
  8. Tomasz Łakomy revised this gist Mar 21, 2020. 1 changed file with 20 additions and 16 deletions.
    36 changes: 20 additions & 16 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -358,25 +358,29 @@ export class TodoDatabase extends cdk.Construct {
    afterwards we need to make the `todoHandler` available for modules importing this class:

    ```
    export class TodoAdder extends cdk.Construct {
    public readonly handler: lambda.Function;
    import * as cdk from "@aws-cdk/core";
    import * as dynamodb from "@aws-cdk/aws-dynamodb";
    import * as lambda from "@aws-cdk/aws-lambda";
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id);
    export class TodoDatabase extends cdk.Construct {
    public readonly handler: lambda.Function;
    const todosTable = new dynamodb.Table(this, "Todos", {
    partitionKey: { name: "todo", type: dynamodb.AttributeType.STRING }
    });
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id);
    this.handler = new lambda.Function(this, "TodoAdderHandler", {
    runtime: lambda.Runtime.NODEJS_10_X,
    handler: "todoAdder-lambda.handler",
    code: lambda.Code.fromAsset("lambda"),
    environment: {
    TODOS_TABLE_NAME: todosTable.tableName
    }
    });
    }
    const todosTable = new dynamodb.Table(this, "TodoTable", {
    partitionKey: { name: "id", type: dynamodb.AttributeType.STRING }
    });
    this.handler = new lambda.Function(this, "TodoHandler", {
    code: lambda.Code.fromAsset("lambda"),
    handler: "todoHandler.handler",
    runtime: lambda.Runtime.NODEJS_12_X,
    environment: {
    TABLE_NAME: todosTable.tableName
    }
    });
    }
    }
    ```

  9. Tomasz Łakomy revised this gist Mar 21, 2020. 1 changed file with 18 additions and 22 deletions.
    40 changes: 18 additions & 22 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -331,28 +331,27 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    - Deploy the `todoHandler.ts` function in the `TodoDatabase` stack

    ```
    import cdk = require("@aws-cdk/core");
    import lambda = require("@aws-cdk/aws-lambda");
    import dynamodb = require("@aws-cdk/aws-dynamodb");
    export class TodoAdder extends cdk.Construct {
    import * as cdk from "@aws-cdk/core";
    import * as dynamodb from "@aws-cdk/aws-dynamodb";
    import * as lambda from "@aws-cdk/aws-lambda";
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id);
    export class TodoDatabase extends cdk.Construct {
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id);
    const todosTable = new dynamodb.Table(this, "Todos", {
    partitionKey: { name: "todo", type: dynamodb.AttributeType.STRING }
    });
    const todosTable = new dynamodb.Table(this, "TodoTable", {
    partitionKey: { name: "id", type: dynamodb.AttributeType.STRING }
    });
    new lambda.Function(this, "TodoAdderHandler", {
    runtime: lambda.Runtime.NODEJS_10_X,
    handler: "todoAdder-lambda.handler",
    code: lambda.Code.fromAsset("lambda"),
    environment: {
    TODOS_TABLE_NAME: todosTable.tableName
    }
    });
    }
    new lambda.Function(this, "TodoHandler", {
    code: lambda.Code.fromAsset("lambda"),
    handler: "todoHandler.handler",
    runtime: lambda.Runtime.NODEJS_12_X,
    environment: {
    TABLE_NAME: todosTable.tableName
    }
    });
    }
    }
    ```

    @@ -377,9 +376,6 @@ export class TodoAdder extends cdk.Construct {
    TODOS_TABLE_NAME: todosTable.tableName
    }
    });
    // Grant the lambda role read/write permissions to this table
    todosTable.grantReadWriteData(this.handler);
    }
    }
    ```
  10. Tomasz Łakomy revised this gist Mar 21, 2020. 1 changed file with 41 additions and 1 deletion.
    42 changes: 41 additions & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -286,7 +286,47 @@ Resources

    - Create a `todoHandler.ts` lambda function that for now is going to list all items from the table:

    **REWRITE THIS SECTION**
    ```
    /// <reference types="aws-sdk" />
    import AWS = require("aws-sdk");
    const tableName = process.env.TABLE_NAME || "";
    const dynamo = new AWS.DynamoDB.DocumentClient();
    const createResponse = (
    body: string | AWS.DynamoDB.DocumentClient.ItemList,
    statusCode = 200
    ) => {
    return {
    statusCode,
    body: JSON.stringify(body, null, 2)
    };
    };
    const getAllTodos = async () => {
    // Implement me!
    // Check out https://github.com/dabit3/dynamodb-documentclient-cheat-sheet
    };
    exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    try {
    const { httpMethod, body: requestBody } = event;
    if (httpMethod === "GET") {
    const response = await getAllTodos();
    return createResponse(response.Items || []);
    }
    return createResponse(
    `We only accept GET requests for now, not ${httpMethod}`,
    500
    );
    } catch (error) {
    console.log(error);
    return createResponse(error, 500);
    }
    };
    ```

    - Deploy the `todoHandler.ts` function in the `TodoDatabase` stack

  11. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,8 @@

    Create an AWS account, install AWS CLI, AWS CDK, AWS SAM (including Docker) and configure your account

    Hide `node_modules`, `**/*.d.ts` and `**/lesson*/**/*.js` files in VSCode using `Exclude` option in Settings

    ### Note - check the cdk-spa-deploy version before the workshop because if there's a version mismatch it won't work

    ## Workshop:
  12. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -608,6 +608,10 @@ NewTodoAppStack.Endpoint8024A810 = https://hrqfdwbly9.execute-api.eu-central-1.a

    Build the app using AWS Amplify - as of 04.03.2020, I was unable to do that via CDK because it's still experimental

    # Part 11

    **TESTING!**

    ## Endgame

    - Destroy the stack by running `cdk destroy`
  13. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 15 additions and 1 deletion.
    16 changes: 15 additions & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    ## Pre-workshop:

    Create an AWS account, install AWS CDK and configure your account
    Create an AWS account, install AWS CLI, AWS CDK, AWS SAM (including Docker) and configure your account

    ### Note - check the cdk-spa-deploy version before the workshop because if there's a version mismatch it won't work

    @@ -152,7 +152,21 @@ export class NewTodoAppStack extends cdk.Stack {

    **Execute a lambda function locally**

    - Compile your AWS CDK app and create a AWS CloudFormation template
    - First, run `npm run build` to compile the app
    - Run `cdk synth --no-staging > template.yaml` to create a CloudFormation template
    - Find the logical ID for your Lambda function in template.yaml. It will look like `MyFunction12345678`, where 12345678 represents an 8-character unique ID that the AWS CDK generates for all resources. The line right after it should look like:

    `Type: AWS::Lambda::Function`

    - Run the function by executing:
    `sam local invoke HelloLambda3D9C82D6`

    - We can also pass custom events to the function, to do that - take a look at `sample_events` directory, there's a sample `hello.json` event in there

    - To execute a lambda function locally with a custom event, execute:

    `sam local invoke HelloLambda3D9C82D6 -e sample_events/hello.json`

    ## Part 3

  14. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 10 additions and 0 deletions.
    10 changes: 10 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -148,6 +148,12 @@ export class NewTodoAppStack extends cdk.Stack {
    - Using env variables in lambda functions
    - Attaching API Gateway to a lambda function and creating a REST endpoint

    ## Part 2.5

    **Execute a lambda function locally**



    ## Part 3

    **Working with S3**
    @@ -584,6 +590,10 @@ NewTodoAppStack.Endpoint8024A810 = https://hrqfdwbly9.execute-api.eu-central-1.a
    - We don't have to do everything ourselves, we can use constructs made by community
    - If we want, we can create an entire CDN distribution without leaving our editor

    # Part 10

    Build the app using AWS Amplify - as of 04.03.2020, I was unable to do that via CDK because it's still experimental

    ## Endgame

    - Destroy the stack by running `cdk destroy`
  15. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 14 additions and 0 deletions.
    14 changes: 14 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -569,6 +569,20 @@ NewTodoAppStack.Endpoint8024A810 = https://hrqfdwbly9.execute-api.eu-central-1.a
    # Part 9 - use CDK-SPA-Deploy to avoid Re:Inventing (hah) the wheel:

    - Run `npm install --save cdk-spa-deploy` in `frontend` directory https://github.com/nideveloper/CDK-SPA-Deploy
    - CDK-SPA-Deploy can either deploy a `basic` site or a full CloudFront distribution. Deploying a new distribution takes ~10 minutes so stick to a basic site instead:

    ```
    // Using the SPA Deploy construct from npm:
    new SPADeploy(this, "spaDeploy").createBasicSite({
    indexDoc: "index.html",
    websiteFolder: "../frontend/build"
    });
    ```

    **What we've learned in this section:**
    - We don't have to do everything ourselves, we can use constructs made by community
    - If we want, we can create an entire CDN distribution without leaving our editor

    ## Endgame

  16. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -561,15 +561,15 @@ NewTodoAppStack.WebsiteUrl = http://newtodoappstack-websitebucket75c24d94-sod2pi
    NewTodoAppStack.Endpoint8024A810 = https://hrqfdwbly9.execute-api.eu-central-1.amazonaws.com/prod/
    ```

    # Part 9 - use CDK-SPA-Deploy to avoid Re:Inventing (hah) the wheel:

    - Run `npm install --save cdk-spa-deploy` in `frontend` directory

    **What we've learned in this section:**

    - Creating a S3 Bucket deployment to host a static website
    - Creating a custom CloudFormation stack output

    # Part 9 - use CDK-SPA-Deploy to avoid Re:Inventing (hah) the wheel:

    - Run `npm install --save cdk-spa-deploy` in `frontend` directory https://github.com/nideveloper/CDK-SPA-Deploy

    ## Endgame

    - Destroy the stack by running `cdk destroy`
  17. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    Create an AWS account, install AWS CDK and configure your account

    # Note - check the cdk-spa-deploy before the workshop because if there's a version mismatch it won't work
    ### Note - check the cdk-spa-deploy version before the workshop because if there's a version mismatch it won't work

    ## Workshop:

  18. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,8 @@

    Create an AWS account, install AWS CDK and configure your account

    # Note - check the cdk-spa-deploy before the workshop because if there's a version mismatch it won't work

    ## Workshop:

    ## Part 1
  19. Tomasz Łakomy revised this gist Mar 4, 2020. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -559,6 +559,10 @@ NewTodoAppStack.WebsiteUrl = http://newtodoappstack-websitebucket75c24d94-sod2pi
    NewTodoAppStack.Endpoint8024A810 = https://hrqfdwbly9.execute-api.eu-central-1.amazonaws.com/prod/
    ```

    # Part 9 - use CDK-SPA-Deploy to avoid Re:Inventing (hah) the wheel:

    - Run `npm install --save cdk-spa-deploy` in `frontend` directory

    **What we've learned in this section:**

    - Creating a S3 Bucket deployment to host a static website
  20. Tomasz Łakomy revised this gist Feb 11, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -112,7 +112,7 @@ and `console.log` it in the lambda function:
    **But how do we call this function?**

    - Run `npm install @aws-cdk/aws-apigateway`
    - Add an API Gateway to oru stack, so it looks like this:
    - Add an API Gateway to our stack, so it looks like this:

    ```
    import * as cdk from "@aws-cdk/core";
  21. Tomasz Łakomy revised this gist Feb 10, 2020. 1 changed file with 85 additions and 162 deletions.
    247 changes: 85 additions & 162 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -262,28 +262,7 @@ Resources

    - Create a `todoHandler.ts` lambda function that for now is going to list all items from the table:

    ```
    const { DynamoDB } = require("aws-sdk");
    exports.handler = async function(event: Record<string, any>) {
    const dynamo = new DynamoDB();
    const scanResult = await dynamo
    .scan({
    "TableName": process.env.TABLE_NAME,
    "Limit": 10
    })
    .promise();
    const response = scanResult.Items.map(DynamoDB.Converter.unmarshall);
    return {
    statusCode: 200,
    headers: { "Content-Type": "text/plain" },
    body: JSON.stringify(response, null, 2)
    };
    };
    ```
    **REWRITE THIS SECTION**

    - Deploy the `todoHandler.ts` function in the `TodoDatabase` stack

    @@ -401,175 +380,119 @@ and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda funct
    ## Part 6 - Adding and removing data from Todos DynamoDB table

    - Refactor the `todoHandler` function so it can handle both `GET` and `POST` requests, along with some error handling (notice that currently the id is **HARDCODED**):

    **REWRITE THIS SECTION**

    - Go to the lambda directory and run `npm init` to create a `package.json` and install `uuid` package
    - Modify the `POST` section of the lambda function to use the `uuid` package:

    **REWRITE THIS SECTION**

    - Add a `DELETE` handler so we'll be able to delete a todo item:

    **REWRITE THIS SECTION**

    - At the end, our lambda function should look similar to this:

    ```
    const { DynamoDB } = require("aws-sdk");
    const uuid = require("uuid");
    const createResponse = (body: string, statusCode = 200) => {
    return {
    statusCode,
    headers: { "Content-Type": "text/plain" },
    body: JSON.stringify(body, null, 2)
    };
    return {
    statusCode,
    headers: {
    "Content-Type": "text/plain",
    "Access-Control-Allow-Origin": "*" // Required for CORS support to work
    },
    body: JSON.stringify(body, null, 2)
    };
    };
    const tableName = process.env.TABLE_NAME;
    const dynamo = new DynamoDB();
    exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    try {
    const dynamo = new DynamoDB();
    const { queryStringParameters, httpMethod } = event;
    const tableName = process.env.TABLE_NAME;
    if (httpMethod === "GET") {
    const scanResult = await dynamo
    const getAlllTodos = async () => {
    const scanResult = await dynamo
    .scan({
    "TableName": tableName,
    "Limit": 10
    "TableName": tableName,
    "Limit": 10
    })
    .promise();
    const response = scanResult.Items.map(DynamoDB.Converter.unmarshall);
    return createResponse(response);
    }
    return scanResult.Items.map(DynamoDB.Converter.unmarshall);
    };
    if (httpMethod === "POST") {
    if (queryStringParameters && queryStringParameters.todo) {
    const { todo } = queryStringParameters;
    const addTodoItem = async (data: { todo: string; id: string }) => {
    const { id, todo } = data;
    if (todo && todo !== "") {
    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: "random_id" },
    todo: { S: todo }
    }
    })
    .promise();
    return createResponse(`${todo} added to the database`);
    }
    return createResponse("Todo is missing", 400);
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: id || uuid() },
    todo: { S: todo }
    }
    })
    .promise();
    }
    return createResponse(
    `We only accept GET, POST, and DELETE, not ${httpMethod}`,
    400
    );
    } catch (error) {
    return createResponse(error, 400);
    }
    return todo;
    };
    ```
    - Go to the lambda directory and run `npm init` to create a `package.json` and install `uuid` package
    - Modify the `POST` section of the lambda function to use the `uuid` package:
    ```
    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: uuid() },
    todo: { S: todo }
    }
    })
    .promise();
    ```
    - Add a `DELETE` handler so we'll be able to delete a todo item:
    ```
    if (httpMethod === "DELETE") {
    if (queryStringParameters && queryStringParameters.id) {
    const { id } = queryStringParameters;
    const deleteTodoItem = async (data: { id: string }) => {
    const { id } = data;
    if (id && id !== "") {
    await dynamo
    .deleteItem({
    "TableName": tableName,
    "Key": {
    id: { S: id }
    }
    })
    .promise();
    return createResponse(
    `Todo item with an id of ${id} deleted from the database`
    );
    }
    return createResponse("ID is missing", 400);
    .deleteItem({
    "TableName": tableName,
    "Key": {
    id: { S: id }
    }
    })
    .promise();
    }
    ```
    - At the end, our lambda function should look similar to this:
    ```
    const { DynamoDB } = require("aws-sdk");
    const uuid = require("uuid");
    const createResponse = (body: string, statusCode = 200) => {
    return {
    statusCode,
    headers: {
    "Content-Type": "text/plain",
    "Access-Control-Allow-Origin": "*" // Required for CORS support to work
    },
    body: JSON.stringify(body, null, 2)
    };
    return id;
    };
    exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    try {
    const dynamo = new DynamoDB();
    const { queryStringParameters, httpMethod } = event;
    const tableName = process.env.TABLE_NAME;
    try {
    const { httpMethod, body: requestBody } = event;
    if (httpMethod === "GET") {
    const scanResult = await dynamo
    .scan({
    "TableName": tableName,
    "Limit": 10
    })
    .promise();
    if (httpMethod === "GET") {
    const response = await getAlllTodos();
    const response = scanResult.Items.map(DynamoDB.Converter.unmarshall);
    return createResponse(response);
    }
    return createResponse(response);
    }
    if (!requestBody) {
    return createResponse("Missing request body", 400);
    }
    if (httpMethod === "POST") {
    if (queryStringParameters && queryStringParameters.todo) {
    const { todo } = queryStringParameters;
    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: uuid() },
    todo: { S: todo }
    }
    })
    .promise();
    return createResponse(`${todo} added to the database`);
    }
    return createResponse("Todo is missing", 400);
    }
    const data = JSON.parse(requestBody);
    if (httpMethod === "POST") {
    const todo = await addTodoItem(data);
    return todo
    ? createResponse(`${todo} added to the database`)
    : createResponse("Todo is missing", 400);
    }
    if (httpMethod === "DELETE") {
    const id = await deleteTodoItem(data);
    return id
    ? createResponse(
    `Todo item with an id of ${id} deleted from the database`
    )
    : createResponse("ID is missing", 400);
    }
    if (httpMethod === "DELETE") {
    if (queryStringParameters && queryStringParameters.id) {
    const { id } = queryStringParameters;
    await dynamo
    .deleteItem({
    "TableName": tableName,
    "Key": {
    id: { S: id }
    }
    })
    .promise();
    return createResponse(
    `Todo item with an id of ${id} deleted from the database`
    `We only accept GET, POST, and DELETE, not ${httpMethod}`,
    400
    );
    }
    return createResponse("ID is missing", 400);
    } catch (error) {
    return createResponse(error, 400);
    }
    return createResponse(
    `We only accept GET, POST, and DELETE, not ${httpMethod}`,
    400
    );
    } catch (error) {
    return createResponse(error, 400);
    }
    };
    ```
    **What we've learned in this section:**
  22. Tomasz Łakomy revised this gist Feb 10, 2020. 1 changed file with 154 additions and 1 deletion.
    155 changes: 154 additions & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -472,6 +472,115 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    })
    .promise();
    ```
    - Add a `DELETE` handler so we'll be able to delete a todo item:
    ```
    if (httpMethod === "DELETE") {
    if (queryStringParameters && queryStringParameters.id) {
    const { id } = queryStringParameters;
    await dynamo
    .deleteItem({
    "TableName": tableName,
    "Key": {
    id: { S: id }
    }
    })
    .promise();
    return createResponse(
    `Todo item with an id of ${id} deleted from the database`
    );
    }
    return createResponse("ID is missing", 400);
    }
    ```
    - At the end, our lambda function should look similar to this:
    ```
    const { DynamoDB } = require("aws-sdk");
    const uuid = require("uuid");
    const createResponse = (body: string, statusCode = 200) => {
    return {
    statusCode,
    headers: {
    "Content-Type": "text/plain",
    "Access-Control-Allow-Origin": "*" // Required for CORS support to work
    },
    body: JSON.stringify(body, null, 2)
    };
    };
    exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    try {
    const dynamo = new DynamoDB();
    const { queryStringParameters, httpMethod } = event;
    const tableName = process.env.TABLE_NAME;
    if (httpMethod === "GET") {
    const scanResult = await dynamo
    .scan({
    "TableName": tableName,
    "Limit": 10
    })
    .promise();
    const response = scanResult.Items.map(DynamoDB.Converter.unmarshall);
    return createResponse(response);
    }
    if (httpMethod === "POST") {
    if (queryStringParameters && queryStringParameters.todo) {
    const { todo } = queryStringParameters;
    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: uuid() },
    todo: { S: todo }
    }
    })
    .promise();
    return createResponse(`${todo} added to the database`);
    }
    return createResponse("Todo is missing", 400);
    }
    if (httpMethod === "DELETE") {
    if (queryStringParameters && queryStringParameters.id) {
    const { id } = queryStringParameters;
    await dynamo
    .deleteItem({
    "TableName": tableName,
    "Key": {
    id: { S: id }
    }
    })
    .promise();
    return createResponse(
    `Todo item with an id of ${id} deleted from the database`
    );
    }
    return createResponse("ID is missing", 400);
    }
    return createResponse(
    `We only accept GET, POST, and DELETE, not ${httpMethod}`,
    400
    );
    } catch (error) {
    return createResponse(error, 400);
    }
    };
    ```
    **What we've learned in this section:**

    Okay, that section is not easy

    - Adding a new item to a DynamoDB table using `PutItem`
    - Using external packages (in this case - `uuid` in lambda functions created with CDK)
    - Deleting an item from DynamoDB table using `deleteItem`

    **Good news - we are not going to touch dynamoDB anymore!**

    ## Part 7 - Frontend integration

    @@ -488,10 +597,54 @@ headers: {
    ```
    - Refresh the frontend app and notice that the data is loaded


    **What we've learned in this section:**

    - Connecting our new backend to frontend by plugging it into `.env`
    - Enabling CORS in our lambda function

    # Part 8 - Deploying the static page to an S3 bucket
    - Build the frontend app by running `npm run build`
    - Install `@aws-cdk/aws-s3-deployment` from npm
    - Go to our stack and create a new bucket for the website and configure a deployment
    ```
    const websiteBucket = new s3.Bucket(this, "WebsiteBucket", {
    publicReadAccess: true,
    websiteIndexDocument: "index.html"
    });
    ```

    ```
    new s3Deployment.BucketDeployment(this, "DeployWebsite", {
    sources: [s3Deployment.Source.asset("../frontend/build")],
    destinationBucket: websiteBucket
    });
    ```

    **Okay, but where the hell is my website?**
    - In order to find out website without digging into S3, we're going to add a new output to our stack using `CfnOutput`:
    ```
    new cdk.CfnOutput(this, "WebsiteUrl", {
    value: websiteBucket.bucketWebsiteUrl
    });
    ```
    - which will give us a result similar to this one:

    ```
    Outputs:
    NewTodoAppStack.WebsiteUrl = http://newtodoappstack-websitebucket75c24d94-sod2pi006ey5.s3-website.eu-central-1.amazonaws.com
    NewTodoAppStack.Endpoint8024A810 = https://hrqfdwbly9.execute-api.eu-central-1.amazonaws.com/prod/
    ```

    **What we've learned in this section:**

    - Creating a S3 Bucket deployment to host a static website
    - Creating a custom CloudFormation stack output

    ## Endgame

    - Destroy the stack by running `cdk destroy`
    > I've used `cdk` to destroy `cdk`
    > I've used `cdk` to destroy `cdk` - Tony Stark
    ## Useful tips:
    Add a following .prettierc file to the project to avoid problems with DynamoDB SDK:
  23. Tomasz Łakomy revised this gist Feb 10, 2020. 1 changed file with 11 additions and 2 deletions.
    13 changes: 11 additions & 2 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -475,9 +475,18 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {

    ## Part 7 - Frontend integration

    - Open the `frontend` directory, run the TypeScript React app inside
    - Open the `frontend` directory, run the TypeScript React app inside with `yarn start`
    - Inject the API endpoint into the app to try to fetch current todos

    - Notice that backend doesn't work due to CORS
    - Go back to our stack and add CORS preflight options to our REST API. The only thing we need to add is a header in the lambda response since CORS is automatically enabled on the API Gateway level when using `lambdaRestApi` (source: https://github.com/aws/aws-cdk/issues/906).
    - Add following headers to the lambda function response:
    ```
    headers: {
    "Content-Type": "text/plain",
    "Access-Control-Allow-Origin": "*" // Required for CORS support to work
    },
    ```
    - Refresh the frontend app and notice that the data is loaded

    ## Endgame

  24. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 15 additions and 57 deletions.
    72 changes: 15 additions & 57 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -400,7 +400,7 @@ and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda funct

    ## Part 6 - Adding and removing data from Todos DynamoDB table

    - Refactor the `todoHandler` function so it can handle both `GET` and `POST` requests, along with some error handling:
    - Refactor the `todoHandler` function so it can handle both `GET` and `POST` requests, along with some error handling (notice that currently the id is **HARDCODED**):
    ```
    const { DynamoDB } = require("aws-sdk");
    @@ -458,68 +458,26 @@ exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    }
    };
    ```
    - Go to the lambda directory and run `npm init` to create a `package.json` and install `uuid` package
    - Modify the `POST` section of the lambda function to use the `uuid` package:

    ## Part 7 - Frontend integration

    - Open the `frontend` directory, run the TypeScript React app inside
    - Inject the API endpoint into the app to try to fetch current todos
    - It's going to fail because of CORS, we need to return a proper header in our lambda function to make it work:
    ```return {
    statusCode,
    headers: {
    "Content-Type": "text/plain",
    "Access-Control-Allow-Origin": "*"
    },
    body: JSON.stringify(response, null, 2)
    };
    ```

    - Update lambda function so it is able to both add an item or return the latest 10 todos:

    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: uuid() },
    todo: { S: todo }
    }
    })
    .promise();
    ```
    const { DynamoDB } = require("aws-sdk");

    exports.handler = async function(event) {
    const dynamo = new DynamoDB();
    const { queryStringParameters } = event;
    let response = "OK";
    let statusCode = 200;
    console.log(queryStringParameters);
    ## Part 7 - Frontend integration

    try {
    if (queryStringParameters && queryStringParameters.todo) {
    console.log("Putting a new item into DynamoDB");
    await dynamo
    .putItem({
    "TableName": process.env.TODOS_TABLE_NAME,
    "Item": {
    todo: { S: queryStringParameters.todo }
    }
    })
    .promise();
    } else {
    console.log("Scanning all items from the DB");
    const scanResult = await dynamo
    .scan({
    "TableName": process.env.TODOS_TABLE_NAME,
    "Limit": 10
    })
    .promise();
    response = scanResult.Items;
    }
    } catch (error) {
    console.log(error);
    response = error;
    statusCode = 500;
    }
    - Open the `frontend` directory, run the TypeScript React app inside
    - Inject the API endpoint into the app to try to fetch current todos

    return {
    statusCode,
    headers: { "Content-Type": "text/plain" },
    body: response
    };
    };
    ```

    ## Endgame

  25. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 59 additions and 0 deletions.
    59 changes: 59 additions & 0 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -400,6 +400,65 @@ and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda funct

    ## Part 6 - Adding and removing data from Todos DynamoDB table

    - Refactor the `todoHandler` function so it can handle both `GET` and `POST` requests, along with some error handling:
    ```
    const { DynamoDB } = require("aws-sdk");
    const createResponse = (body: string, statusCode = 200) => {
    return {
    statusCode,
    headers: { "Content-Type": "text/plain" },
    body: JSON.stringify(body, null, 2)
    };
    };
    exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    try {
    const dynamo = new DynamoDB();
    const { queryStringParameters, httpMethod } = event;
    const tableName = process.env.TABLE_NAME;
    if (httpMethod === "GET") {
    const scanResult = await dynamo
    .scan({
    "TableName": tableName,
    "Limit": 10
    })
    .promise();
    const response = scanResult.Items.map(DynamoDB.Converter.unmarshall);
    return createResponse(response);
    }
    if (httpMethod === "POST") {
    if (queryStringParameters && queryStringParameters.todo) {
    const { todo } = queryStringParameters;
    await dynamo
    .putItem({
    "TableName": tableName,
    "Item": {
    id: { S: "random_id" },
    todo: { S: todo }
    }
    })
    .promise();
    return createResponse(`${todo} added to the database`);
    }
    return createResponse("Todo is missing", 400);
    }
    return createResponse(
    `We only accept GET, POST, and DELETE, not ${httpMethod}`,
    400
    );
    } catch (error) {
    return createResponse(error, 400);
    }
    };
    ```

    ## Part 7 - Frontend integration

    - Open the `frontend` directory, run the TypeScript React app inside
  26. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -18,10 +18,11 @@ Create an AWS account, install AWS CDK and configure your account
    - Delete all the boilerplate code and run `cdk diff` to see the resources that will be deleted
    - Deploy the stack again
    - Check out CloudFront to see an empty stack
    - Install `npm install --save @types/aws-lambda`
    - Create a new lambda function handler in new `lambda` directory, call it `hello.ts`

    ```
    exports.handler = async function(event: Record<string, any>) {
    exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
    console.log("request:", JSON.stringify(event, null, 2));
    return {
  27. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 9 additions and 1 deletion.
    10 changes: 9 additions & 1 deletion Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -464,4 +464,12 @@ exports.handler = async function(event) {
    ## Endgame

    - Destroy the stack by running `cdk destroy`
    > I've used `cdk` to destroy `cdk`
    > I've used `cdk` to destroy `cdk`
    ## Useful tips:
    Add a following .prettierc file to the project to avoid problems with DynamoDB SDK:
    ```
    {
    "quoteProps": "preserve"
    }
    ```
  28. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -257,7 +257,7 @@ Resources
    - Creating a DynamoDB table
    - Adding new items to a DynamoDB table

    ## Part 5 - Adding DynamoDB backend
    ## Part 5 - Creating a DynamoDB table for todos

    - Create a `todoHandler.ts` lambda function that for now is going to list all items from the table:

    @@ -397,7 +397,9 @@ and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda funct
    - Debugging permission issues using CloudWatch logs
    - Granting DynamoDB read/write priviledges to a lambda function (with a single line of code, holy shit)

    ## Next up - frontend
    ## Part 6 - Adding and removing data from Todos DynamoDB table

    ## Part 7 - Frontend integration

    - Open the `frontend` directory, run the TypeScript React app inside
    - Inject the API endpoint into the app to try to fetch current todos
  29. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 13 additions and 7 deletions.
    20 changes: 13 additions & 7 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -251,6 +251,12 @@ Resources
    }
    ```

    **What we've learned in this section:**

    - Creating a new CDK Construct from scratch and importing it in main stack
    - Creating a DynamoDB table
    - Adding new items to a DynamoDB table

    ## Part 5 - Adding DynamoDB backend

    - Create a `todoHandler.ts` lambda function that for now is going to list all items from the table:
    @@ -377,19 +383,19 @@ export class NewTodoAppStack extends cdk.Stack {

    - Grant the lambda role read/write permissions to table in `todoadder-stack`:
    ```
    // Grant the lambda role read/write permissions to this table
    todosTable.grantReadWriteData(this.handler);
    // Grant the lambda function role read/write permissions to this table
    todoDatabase.grantReadWriteData(this.todoHandler);
    ```

    and run `cdk diff` to see `Get/Put/UpdateItem` etc. allowed for the lambda function and deploy the stack

    - Test the function and notice data being added to the DynamoDB
    - Test the function and notice data being read from DynamoDB

    **This concluded the backend/infra major part of the workshop - what we've learned in this section:**
    **What we've learned in this section:**

    - Creating a new CDK Construct from scratch and importing it in main stack
    - Updating a DynamoDB table
    - Granting DynamoDB read/write priviledges to a lambda function
    - Scanning a DynamoDB table using `aws-cdk`
    - Debugging permission issues using CloudWatch logs
    - Granting DynamoDB read/write priviledges to a lambda function (with a single line of code, holy shit)

    ## Next up - frontend

  30. Tomasz Łakomy revised this gist Feb 9, 2020. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions Egghead AWS CDK Workshop Rough Plan.markdown
    Original file line number Diff line number Diff line change
    @@ -366,15 +366,16 @@ export class NewTodoAppStack extends cdk.Stack {
    }
    }
    ```
    - Take a look at the `cdk diff`, that's A LOT of changes we don't have to do ourselves
    - Take a look at the `cdk diff`, that's **A LOT** of changes we don't have to do ourselves

    - Deploy the function and `curl -i` the endpoint which is going to fail
    - Debug WTF is going in using CloudWatch
    - Shit's fucked:

    ` "errorMessage": "User: arn:aws:sts::696785635119:assumed-role/TodoAppStack-HelloHitCounterHitCounterHandlerServi-1FJDMDD4Z1TUC/TodoAppStack-HelloHitCounterHitCounterHandlerDAEA7-QK783PR45D9T is not authorized to perform: dynamodb:UpdateItem on resource: arn:aws:dynamodb:us-east-1:696785635119:table/TodoAppStack-HelloHitCounterHits7AAEBF80-1QMDY1CPG23JX",`
    `"errorMessage": "User: arn:aws:sts::696785635119:assumed-role/NewTodoAppStack-TodoDatabasetodoHandlerServiceRole-1D3JXVMXTHV5G/NewTodoAppStack-TodoDatabasetodoHandlerA2559B62-IDSJIZW758II is not authorized to perform: dynamodb:Scan on resource: arn:aws:dynamodb:eu-central-1:696785635119:table/NewTodoAppStack-TodoDatabase08DB7F4F-F654XN49LOJF",
    `

    - Grant the lambda role read/write peromissions to table in `todoadder-stack`:
    - Grant the lambda role read/write permissions to table in `todoadder-stack`:
    ```
    // Grant the lambda role read/write permissions to this table
    todosTable.grantReadWriteData(this.handler);