From e211246bfa0fcede06c62ce241ae68356c5598de Mon Sep 17 00:00:00 2001 From: Thi Date: Sat, 22 Jul 2023 12:03:19 +0700 Subject: [PATCH 1/9] Add new patterns --- .../cdk/.gitignore | 8 + .../cdk/.npmignore | 6 + .../cdk/README.md | 111 +++++++++++ .../cdk/bin/ApiDynamoDB.ts | 21 ++ .../cdk/cdk.json | 39 ++++ .../cdk/lib/ApiDynamoDBStack.ts | 185 ++++++++++++++++++ .../cdk/package.json | 29 +++ .../cdk/tsconfig.json | 30 +++ 8 files changed, 429 insertions(+) create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/.gitignore create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/.npmignore create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/bin/ApiDynamoDB.ts create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/cdk.json create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/tsconfig.json diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/.gitignore b/apigw-rest-api-dynamodb-lambda-cdk/cdk/.gitignore new file mode 100644 index 000000000..f60797b6a --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/.npmignore b/apigw-rest-api-dynamodb-lambda-cdk/cdk/.npmignore new file mode 100644 index 000000000..c1d6d45dc --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md new file mode 100644 index 000000000..b27c9f90d --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md @@ -0,0 +1,111 @@ +# Amazon API Gateway REST API to Amazon DynamoDB + +This pattern creates an Amazon API Gateway REST API that integrates with an Amazon DynamoDB table. + +Learn more about this pattern at Serverless Land Patterns: http://serverlessland.com/patterns/apigw-dynamodb-lambda-cdk. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-rest-api-dynamodb-lambda-cdk/cdk + ``` + +1. Install dependencies + ``` + npm install + ``` + +1. Deploy the stack to your default AWS account and region. The output of this command should give you the HTTP API URL. + ``` + cdk deploy + ``` + +## How it works + +This pattern creates an Amazon API Gateway REST API that integrates with an Amazon DynamoDB table. The API integrates directly with the DynamoDB API and supports [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) and [Query](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) actions. The API Integration has read and write access to the DynamoDB. There is no authentication on the API endpoint + +## Testing + +Upon deployment, you will see the API endpoint URL in the output. It will take the format: + +`https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com` + +Once the application is deployed, use [Postman](https://www.postman.com/) to test the API using the following instructions. + +1. Launch Postman + +1. Invoke the DynamoDB **PutItem** action to add a new item to the DynamoDB table: + * Enter the API URL with the **prod** stage as the path:. + ``` + https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/id + ``` + * Select **POST** as the HTTP method from the drop-down list to the left of the address bar. + * Choose the **Body** tab. Choose **raw** and select **JSON** from the drop-down list. Enter the following into the text box: + ``` + { + "pk": "foo", + "data": "blah blah blah" + } + ``` + * Choose **Send** to submit the request and receive a "200 OK" response. + * Open the DynamoDB console and select the table which was created to confirm that the item has been added. + * Change the values for `pk` or `data` in the POST body and repeat this process to add multiple items to the DynamoDB table. + +1. Invoke the DynamoDB **Query** action to query items by artist in the DynamoDB table: + * Enter the Invoke URL in the address bar. Add **/prod/foo** to the URL path. + * Add **/foo** to the URL path. This defines the ID that you want to query. + ``` + https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/${pk} + ``` + * Select **GET** as the HTTP method from the drop-down list to the left of the address bar. + * Choose the **Body** tab. Choose **none**. + * Choose **Send** to submit the request and receive a "200 OK" response with a list of the matching results. Example: + ``` + { + "Count": 1, + "Items": [ + { + "pk": { + "S": "foo" + }, + "data": { + "S": "blah blah blah" + } + } + ], + "ScannedCount": 1 + } + ``` +## Documentation +- [Tutorial: Build an API Gateway REST API with AWS integration](https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-aws-proxy.html) +- [How do I use API Gateway as a proxy for another AWS service?](https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-proxy-integrate-service/) +- [Using Amazon API Gateway as a proxy for DynamoDB](https://aws.amazon.com/blogs/compute/using-amazon-api-gateway-as-a-proxy-for-dynamodb/) +- [Setting up data transformations for REST APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-data-transformations.html) +- [Amazon API Gateway API request and response data mapping reference](https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html) +- [API Gateway mapping template and access logging variable reference](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html) + +## Cleanup + +Run the given command to delete the resources that were created. It might take some time for the CloudFormation stack to get deleted. +``` +cdk destroy +``` + +---- +Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/bin/ApiDynamoDB.ts b/apigw-rest-api-dynamodb-lambda-cdk/cdk/bin/ApiDynamoDB.ts new file mode 100644 index 000000000..5955ad9c6 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/bin/ApiDynamoDB.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { ApiDynamoDBStack } from '../lib/ApiDynamoDBStack'; + +const app = new cdk.App(); +new ApiDynamoDBStack(app, 'ApiDynamoDBStack', { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ + + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + // env: { account: '123456789012', region: 'us-east-1' }, + + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); \ No newline at end of file diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/cdk.json b/apigw-rest-api-dynamodb-lambda-cdk/cdk/cdk.json new file mode 100644 index 000000000..cbc12db91 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/cdk.json @@ -0,0 +1,39 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/ApiDynamoDB.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, + "@aws-cdk/core:stackRelativeExports": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-lambda:recognizeVersionProps": true, + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ] + } +} diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts b/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts new file mode 100644 index 000000000..48ce358c1 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts @@ -0,0 +1,185 @@ +import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { AwsIntegration, Cors, RestApi } from 'aws-cdk-lib/aws-apigateway'; +import { Table, BillingMode, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; +import { Effect, Policy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { Construct } from 'constructs'; + +export class ApiDynamoDBStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const modelName = 'AWSomeDynamoDB'; + const dynamoDBTable = new Table(this, modelName, { + billingMode: BillingMode.PAY_PER_REQUEST, + partitionKey: { + name:`${modelName}Id`, + type: AttributeType.STRING + }, + removalPolicy: RemovalPolicy.DESTROY, + tableName: modelName + }); + + const getPolicy = new Policy(this, 'getPolicy', { + statements: [ + new PolicyStatement({ + actions: ['dynamodb:GetItem'], + effect: Effect.ALLOW, + resources: [dynamoDBTable.tableArn], + }), + ], + }); + + const putPolicy = new Policy(this, 'putPolicy', { + statements: [ + new PolicyStatement({ + actions: ['dynamodb:PutItem'], + effect: Effect.ALLOW, + resources: [dynamoDBTable.tableArn], + }), + ], + }); + + const scanPolicy = new Policy(this, 'scanPolicy', { + statements: [ + new PolicyStatement({ + actions: ['dynamodb:Scan'], + effect: Effect.ALLOW, + resources: [dynamoDBTable.tableArn], + }), + ], + }); + + const api = new RestApi(this, `${modelName}Api`,{ + defaultCorsPreflightOptions:{ + allowOrigins: Cors.ALL_ORIGINS + }, + restApiName:`${modelName} Service` + }) + + const getRole = new Role(this, 'getRole', { + assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), + }); + getRole.attachInlinePolicy(getPolicy); + + const putRole = new Role(this, 'putRole', { + assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), + }); + putRole.attachInlinePolicy(putPolicy); + + const scanRole = new Role(this, 'scanRole', { + assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), + }); + scanRole.attachInlinePolicy(scanPolicy); + + + const errorResponses = [ + { + selectionPattern: '400', + statusCode: '400', + responseTemplates: { + 'application/json': `{ + "error": "Bad input!" + }`, + }, + }, + { + selectionPattern: '5\\d{2}', + statusCode: '500', + responseTemplates: { + 'application/json': `{ + "error": "Internal Service Error!" + }`, + }, + }, + ]; + + const integrationResponses = [ + { + statusCode: '200', + }, + ...errorResponses, + ]; + + const getIntegration = new AwsIntegration({ + action: 'GetItem', + options: { + credentialsRole: getRole, + integrationResponses, + requestTemplates: { + 'application/json': `{ + "Key": { + "${modelName}Id": { + "S": "$method.request.path.id" + } + }, + "TableName": "${modelName}" + }`, + }, + }, + service: 'dynamodb', + }); + + const createIntegration = new AwsIntegration({ + action: 'PutItem', + options: { + credentialsRole: putRole, + integrationResponses: [ + { + statusCode: '200', + responseTemplates: { + 'application/json': `{ + "requestId": "$context.requestId" + }`, + }, + }, + ...errorResponses, + ], + requestTemplates: { + 'application/json': `{ + "Item": { + "${modelName}Id": { + "S": "$context.requestId" + }, + "Name": { + "S": "$input.path('$.name')" + }, + "Description": { + "S": "$input.path('$.description')" + }, + "Customer": { + "S": "$input.path('$.customer')" + }, + "Id": { + "S": "$input.path('$.id')" + } + }, + "TableName": "${modelName}" + }`, + }, + }, + service: 'dynamodb', + }); + + const getAllIntegration = new AwsIntegration({ + action: 'Scan', + options: { + credentialsRole: scanRole, + integrationResponses, + requestTemplates: { + 'application/json': `{ + "TableName": "${modelName}" + }`, + }, + }, + service: 'dynamodb', + }); + + const methodOptions = { methodResponses: [{ statusCode: '200' }, { statusCode: '400' }, { statusCode: '500' }] }; + const allResources = api.root.addResource(modelName.toLocaleLowerCase()); + const oneResource = allResources.addResource('{id}'); + + allResources.addMethod('GET', getAllIntegration, methodOptions); + allResources.addMethod('POST', createIntegration, methodOptions); + oneResource.addMethod('GET', getIntegration, methodOptions); + } +} diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json b/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json new file mode 100644 index 000000000..d72447d42 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json @@ -0,0 +1,29 @@ +{ + "name": "cdk", + "version": "0.1.0", + "bin": { + "cdk": "bin/cdk.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "lint": "eslint . --ext .ts", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^27.5.2", + "@types/node": "10.17.27", + "@types/prettier": "2.6.0", + "aws-cdk": "2.33.0", + "jest": "^27.5.1", + "ts-jest": "^27.1.4", + "ts-node": "^10.9.1", + "typescript": "~4.6.3" + }, + "dependencies": { + "aws-cdk-lib": "2.33.0", + "constructs": "^10.0.0", + "eslint": "^8.45.0", + "source-map-support": "^0.5.21" + } +} diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/tsconfig.json b/apigw-rest-api-dynamodb-lambda-cdk/cdk/tsconfig.json new file mode 100644 index 000000000..9f8e8beab --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": [ + "es2018" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} From 463d94657646beadb3f3715684495a0c80f8aecf Mon Sep 17 00:00:00 2001 From: Thi Date: Sat, 22 Jul 2023 18:22:45 +0700 Subject: [PATCH 2/9] Add Lambda function to handle stream --- .../cdk/example-pattern.json | 61 +++++++++++++++++++ .../cdk/lib/ApiDynamoDBStack.ts | 20 +++++- .../cdk/package.json | 1 + .../cdk/src/index.ts | 6 ++ 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/example-pattern.json create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/example-pattern.json b/apigw-rest-api-dynamodb-lambda-cdk/cdk/example-pattern.json new file mode 100644 index 000000000..52b99a43f --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/example-pattern.json @@ -0,0 +1,61 @@ +{ + "title": "Amazon API Gateway to Amazon DynamoDB and Trigger Event to AWS Lambda as a consumer", + "description": "Create a REST API Gateway which performs PutRecords API to save to DynamoDB and the stream is consumed by a Lambda function", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern will help you to deploy Amazon API Gateway API integration with Amazon DynamoDB and it will deploy Lambda function as a consumer for deployed Amazon DynamoDB Stream.", + "In this pattern, Lambda function is logging the decoded message which was received from the Amazon Kinesis Stream." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-rest-dynamodb-lambda-cdk", + "templateURL": "serverless-patterns/apigw-rest-dynamodb-lambda-cdk", + "projectFolder": "apigw-rest-dynamodb-lambda-cdk", + "templateFile": "apigw-rest-dynamodb-lambda-cdk/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Tutorial: Create a REST API as an Amazon Kinesis proxy in API Gateway", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-kinesis.html" + }, + { + "text": "Kinesis Data Stream PutRecord API", + "link": "https://docs.aws.amazon.com/kinesis/latest/APIReference/API_PutRecord.html" + }, + { + "text": "Using AWS Lambda with Amazon Kinesis", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the Github repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Thi Nguyen", + "image": "https://drive.google.com/file/d/188LpzUvUmHt1o7vzbwKw32S-fYabL-qY/view?usp=sharing", + "bio": "Solutions Architect @ AWS", + "linkedin": "https://www.linkedin.com/in/ndthi" + } + ] + } diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts b/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts index 48ce358c1..f51fca2a7 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts @@ -1,8 +1,10 @@ import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; import { AwsIntegration, Cors, RestApi } from 'aws-cdk-lib/aws-apigateway'; -import { Table, BillingMode, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; +import { Table, BillingMode, AttributeType, StreamViewType } from 'aws-cdk-lib/aws-dynamodb'; import { Effect, Policy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { Construct } from 'constructs'; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; export class ApiDynamoDBStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { @@ -16,9 +18,23 @@ export class ApiDynamoDBStack extends Stack { type: AttributeType.STRING }, removalPolicy: RemovalPolicy.DESTROY, - tableName: modelName + tableName: modelName, + stream: StreamViewType.NEW_AND_OLD_IMAGES }); + const lambdaFunction = new lambda.Function(this, 'Function', { + code: lambda.Code.fromAsset('./src'), + handler: 'index.handler', + functionName: 'TableStreamHandler', + runtime: lambda.Runtime.NODEJS_16_X, + }); + + lambdaFunction.addEventSource(new DynamoEventSource(dynamoDBTable, { + startingPosition: lambda.StartingPosition.LATEST, + })); + + dynamoDBTable.grantStreamRead(lambdaFunction); + const getPolicy = new Policy(this, 'getPolicy', { statements: [ new PolicyStatement({ diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json b/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json index d72447d42..3cff1ee66 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json @@ -21,6 +21,7 @@ "typescript": "~4.6.3" }, "dependencies": { + "@typescript-eslint/eslint-plugin": "^6.1.0", "aws-cdk-lib": "2.33.0", "constructs": "^10.0.0", "eslint": "^8.45.0", diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts b/apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts new file mode 100644 index 000000000..c9b9845e5 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts @@ -0,0 +1,6 @@ +import { Handler } from "aws-cdk-lib/aws-lambda"; +import { DynamoEventSource } from "aws-cdk-lib/aws-lambda-event-sources"; + +export const handler: Handler = async (event: DynamoEventSource) => { + console.log('EVENT: \n' + JSON.stringify(event, null, 2)); +}; \ No newline at end of file From fcc08eafc802f65454cf3f7f5f1c76e702ada5b3 Mon Sep 17 00:00:00 2001 From: Thi Date: Sat, 22 Jul 2023 21:12:17 +0700 Subject: [PATCH 3/9] Clean up --- .../{cdk => }/.gitignore | 0 .../{cdk => }/.npmignore | 0 .../{cdk => }/README.md | 3 +++ .../{cdk => }/bin/ApiDynamoDB.ts | 0 .../{cdk => }/cdk.json | 0 .../{cdk => }/example-pattern.json | 0 .../img/concept.png | Bin 0 -> 32455 bytes .../{cdk => }/lib/ApiDynamoDBStack.ts | 5 +++-- .../{cdk => }/package.json | 0 .../{cdk => }/src/index.ts | 2 +- .../{cdk => }/tsconfig.json | 0 11 files changed, 7 insertions(+), 3 deletions(-) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/.gitignore (100%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/.npmignore (100%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/README.md (97%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/bin/ApiDynamoDB.ts (100%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/cdk.json (100%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/example-pattern.json (100%) create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/img/concept.png rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/lib/ApiDynamoDBStack.ts (98%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/package.json (100%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/src/index.ts (73%) rename apigw-rest-api-dynamodb-lambda-cdk/{cdk => }/tsconfig.json (100%) diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/.gitignore b/apigw-rest-api-dynamodb-lambda-cdk/.gitignore similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/.gitignore rename to apigw-rest-api-dynamodb-lambda-cdk/.gitignore diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/.npmignore b/apigw-rest-api-dynamodb-lambda-cdk/.npmignore similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/.npmignore rename to apigw-rest-api-dynamodb-lambda-cdk/.npmignore diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/README.md similarity index 97% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md rename to apigw-rest-api-dynamodb-lambda-cdk/README.md index b27c9f90d..9cbaa8cc2 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/cdk/README.md +++ b/apigw-rest-api-dynamodb-lambda-cdk/README.md @@ -13,6 +13,9 @@ Important: this application uses various AWS services and there are costs associ * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +## Architecture +The following diagram illustrates the solutions architect +![Architecture Diagram](img/concept.png) ## Deployment Instructions 1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/bin/ApiDynamoDB.ts b/apigw-rest-api-dynamodb-lambda-cdk/bin/ApiDynamoDB.ts similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/bin/ApiDynamoDB.ts rename to apigw-rest-api-dynamodb-lambda-cdk/bin/ApiDynamoDB.ts diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/cdk.json b/apigw-rest-api-dynamodb-lambda-cdk/cdk.json similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/cdk.json rename to apigw-rest-api-dynamodb-lambda-cdk/cdk.json diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/example-pattern.json b/apigw-rest-api-dynamodb-lambda-cdk/example-pattern.json similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/example-pattern.json rename to apigw-rest-api-dynamodb-lambda-cdk/example-pattern.json diff --git a/apigw-rest-api-dynamodb-lambda-cdk/img/concept.png b/apigw-rest-api-dynamodb-lambda-cdk/img/concept.png new file mode 100644 index 0000000000000000000000000000000000000000..bbfe0e41c87d523271bf9b4f96b9c0154c9be0a6 GIT binary patch literal 32455 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 z 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 z z?A@d34PL2%2t36{<^$`>PBpJN#ceF?KZH_~0?1kLH%(;|+{uJLYwK4-sO?#S)yeBC z(-FvS_C4@!tD-qmC^e5xgX@4N$j_`kFi+ETuf`Qx`?+78T`uHSs0daUDpQ{~^%n4h zfD-^sx3N5NA`p$OdNluN!iMAH!~P!RzdwbI|C5poymq2*_rJdgyf9>AMpii0E8O({ z=C^Kaj|}7*vJoa}_x{}^XBi*zy18hAkmG-UN-m_s^!B0;7>1{I^^F z*9ULzssG+2Hy-idW#+#N_21jme|M6bf%yMmFFqAR4)eeGKTZqGRq_i=?yPUNEo%Rq S01v3YrS?oqsY1ar?0*5=%KPg8 literal 0 HcmV?d00001 diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts b/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts similarity index 98% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts rename to apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts index f51fca2a7..3ef491d61 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/cdk/lib/ApiDynamoDBStack.ts +++ b/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts @@ -5,6 +5,7 @@ import { Effect, Policy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk import { Construct } from 'constructs'; import * as lambda from "aws-cdk-lib/aws-lambda"; import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import * as path from 'path'; export class ApiDynamoDBStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { @@ -21,10 +22,10 @@ export class ApiDynamoDBStack extends Stack { tableName: modelName, stream: StreamViewType.NEW_AND_OLD_IMAGES }); - + const lambdaFunction = new lambda.Function(this, 'Function', { code: lambda.Code.fromAsset('./src'), - handler: 'index.handler', + handler: 'index.handlerAAAA', functionName: 'TableStreamHandler', runtime: lambda.Runtime.NODEJS_16_X, }); diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json b/apigw-rest-api-dynamodb-lambda-cdk/package.json similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/package.json rename to apigw-rest-api-dynamodb-lambda-cdk/package.json diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts b/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts similarity index 73% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts rename to apigw-rest-api-dynamodb-lambda-cdk/src/index.ts index c9b9845e5..7aa2c8596 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/cdk/src/index.ts +++ b/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts @@ -2,5 +2,5 @@ import { Handler } from "aws-cdk-lib/aws-lambda"; import { DynamoEventSource } from "aws-cdk-lib/aws-lambda-event-sources"; export const handler: Handler = async (event: DynamoEventSource) => { - console.log('EVENT: \n' + JSON.stringify(event, null, 2)); + console.log('EVENT Content: \n' + JSON.stringify(event, null, 2)); }; \ No newline at end of file diff --git a/apigw-rest-api-dynamodb-lambda-cdk/cdk/tsconfig.json b/apigw-rest-api-dynamodb-lambda-cdk/tsconfig.json similarity index 100% rename from apigw-rest-api-dynamodb-lambda-cdk/cdk/tsconfig.json rename to apigw-rest-api-dynamodb-lambda-cdk/tsconfig.json From 0acddba446599b67ff67638f6a326e979bee61d5 Mon Sep 17 00:00:00 2001 From: Thi Date: Sat, 22 Jul 2023 23:09:28 +0700 Subject: [PATCH 4/9] clean up --- .../lib/ApiDynamoDBStack.ts | 6 +++--- apigw-rest-api-dynamodb-lambda-cdk/package.json | 4 ++-- apigw-rest-api-dynamodb-lambda-cdk/src/index.ts | 2 +- apigw-rest-api-dynamodb-lambda-cdk/src/package.json | 10 ++++++++++ 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/src/package.json diff --git a/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts b/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts index 3ef491d61..075ea4a4f 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts +++ b/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts @@ -24,10 +24,10 @@ export class ApiDynamoDBStack extends Stack { }); const lambdaFunction = new lambda.Function(this, 'Function', { - code: lambda.Code.fromAsset('./src'), - handler: 'index.handlerAAAA', + code: lambda.Code.fromAsset(path.join(__dirname, '../src')), + handler: 'index.handler', functionName: 'TableStreamHandler', - runtime: lambda.Runtime.NODEJS_16_X, + runtime: lambda.Runtime.NODEJS_18_X, }); lambdaFunction.addEventSource(new DynamoEventSource(dynamoDBTable, { diff --git a/apigw-rest-api-dynamodb-lambda-cdk/package.json b/apigw-rest-api-dynamodb-lambda-cdk/package.json index 3cff1ee66..4a055035b 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/package.json +++ b/apigw-rest-api-dynamodb-lambda-cdk/package.json @@ -14,7 +14,7 @@ "@types/jest": "^27.5.2", "@types/node": "10.17.27", "@types/prettier": "2.6.0", - "aws-cdk": "2.33.0", + "aws-cdk": "2.88.0", "jest": "^27.5.1", "ts-jest": "^27.1.4", "ts-node": "^10.9.1", @@ -22,7 +22,7 @@ }, "dependencies": { "@typescript-eslint/eslint-plugin": "^6.1.0", - "aws-cdk-lib": "2.33.0", + "aws-cdk-lib": "2.88.0", "constructs": "^10.0.0", "eslint": "^8.45.0", "source-map-support": "^0.5.21" diff --git a/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts b/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts index 7aa2c8596..459bcc683 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts +++ b/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts @@ -1,6 +1,6 @@ import { Handler } from "aws-cdk-lib/aws-lambda"; import { DynamoEventSource } from "aws-cdk-lib/aws-lambda-event-sources"; -export const handler: Handler = async (event: DynamoEventSource) => { +export const handler = async (event: DynamoEventSource) => { console.log('EVENT Content: \n' + JSON.stringify(event, null, 2)); }; \ No newline at end of file diff --git a/apigw-rest-api-dynamodb-lambda-cdk/src/package.json b/apigw-rest-api-dynamodb-lambda-cdk/src/package.json new file mode 100644 index 000000000..49ad4bcb5 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/src/package.json @@ -0,0 +1,10 @@ +{ + "name": "lambda", + "version": "0.1.0", + "dependencies": { + "aws-cdk-lib": "2.33.0" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.119" + } +} From 7d314af7f2be89609c7ff01c228068e60d396ec5 Mon Sep 17 00:00:00 2001 From: Thi Date: Sun, 23 Jul 2023 01:06:01 +0700 Subject: [PATCH 5/9] Change Lambda runtime to Python --- apigw-rest-api-dynamodb-lambda-cdk/README.md | 3 +++ .../lib/ApiDynamoDBStack.ts | 4 ++-- apigw-rest-api-dynamodb-lambda-cdk/src/index.ts | 6 ------ .../src/messageHandler.py | 10 ++++++++++ apigw-rest-api-dynamodb-lambda-cdk/src/package.json | 10 ---------- .../src/requirements.txt | 2 ++ 6 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 apigw-rest-api-dynamodb-lambda-cdk/src/index.ts create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/src/messageHandler.py delete mode 100644 apigw-rest-api-dynamodb-lambda-cdk/src/package.json create mode 100644 apigw-rest-api-dynamodb-lambda-cdk/src/requirements.txt diff --git a/apigw-rest-api-dynamodb-lambda-cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/README.md index 9cbaa8cc2..cc9e6311c 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/README.md +++ b/apigw-rest-api-dynamodb-lambda-cdk/README.md @@ -30,6 +30,9 @@ The following diagram illustrates the solutions architect 1. Install dependencies ``` npm install + cd src + pip install aws-xray-sdk --target . + pip install aws_lambda_powertools --target . ``` 1. Deploy the stack to your default AWS account and region. The output of this command should give you the HTTP API URL. diff --git a/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts b/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts index 075ea4a4f..86fabb491 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts +++ b/apigw-rest-api-dynamodb-lambda-cdk/lib/ApiDynamoDBStack.ts @@ -25,9 +25,9 @@ export class ApiDynamoDBStack extends Stack { const lambdaFunction = new lambda.Function(this, 'Function', { code: lambda.Code.fromAsset(path.join(__dirname, '../src')), - handler: 'index.handler', + handler: 'messageHandler.lambda_handler', functionName: 'TableStreamHandler', - runtime: lambda.Runtime.NODEJS_18_X, + runtime: lambda.Runtime.PYTHON_3_10, }); lambdaFunction.addEventSource(new DynamoEventSource(dynamoDBTable, { diff --git a/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts b/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts deleted file mode 100644 index 459bcc683..000000000 --- a/apigw-rest-api-dynamodb-lambda-cdk/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Handler } from "aws-cdk-lib/aws-lambda"; -import { DynamoEventSource } from "aws-cdk-lib/aws-lambda-event-sources"; - -export const handler = async (event: DynamoEventSource) => { - console.log('EVENT Content: \n' + JSON.stringify(event, null, 2)); -}; \ No newline at end of file diff --git a/apigw-rest-api-dynamodb-lambda-cdk/src/messageHandler.py b/apigw-rest-api-dynamodb-lambda-cdk/src/messageHandler.py new file mode 100644 index 000000000..d070fbda7 --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/src/messageHandler.py @@ -0,0 +1,10 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 +import os +import boto3 +from aws_lambda_powertools.utilities.data_classes import DynamoDBStreamEvent +from aws_lambda_powertools.utilities.typing import LambdaContext + +def lambda_handler(event: DynamoDBStreamEvent, context: LambdaContext): + records = event["Records"] + print(records) diff --git a/apigw-rest-api-dynamodb-lambda-cdk/src/package.json b/apigw-rest-api-dynamodb-lambda-cdk/src/package.json deleted file mode 100644 index 49ad4bcb5..000000000 --- a/apigw-rest-api-dynamodb-lambda-cdk/src/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "lambda", - "version": "0.1.0", - "dependencies": { - "aws-cdk-lib": "2.33.0" - }, - "devDependencies": { - "@types/aws-lambda": "^8.10.119" - } -} diff --git a/apigw-rest-api-dynamodb-lambda-cdk/src/requirements.txt b/apigw-rest-api-dynamodb-lambda-cdk/src/requirements.txt new file mode 100644 index 000000000..8e0dc717e --- /dev/null +++ b/apigw-rest-api-dynamodb-lambda-cdk/src/requirements.txt @@ -0,0 +1,2 @@ +aws-xray-sdk +aws_lambda_powertools \ No newline at end of file From 8e6c4a90fc59cbfdd6560b091e566a0b42dc4f48 Mon Sep 17 00:00:00 2001 From: Thi Date: Sun, 23 Jul 2023 01:16:15 +0700 Subject: [PATCH 6/9] Update readme --- apigw-rest-api-dynamodb-lambda-cdk/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apigw-rest-api-dynamodb-lambda-cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/README.md index cc9e6311c..a4dd0ee6d 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/README.md +++ b/apigw-rest-api-dynamodb-lambda-cdk/README.md @@ -63,8 +63,10 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes * Choose the **Body** tab. Choose **raw** and select **JSON** from the drop-down list. Enter the following into the text box: ``` { - "pk": "foo", - "data": "blah blah blah" + "id":"14", + "name":"Do some thing", + "description":"Do some thing", + "customer":"John" } ``` * Choose **Send** to submit the request and receive a "200 OK" response. From 57c913821d52a1628cd6e480ba5387d33890abec Mon Sep 17 00:00:00 2001 From: Thi Date: Thu, 3 Aug 2023 13:38:36 +0700 Subject: [PATCH 7/9] Update instruction --- apigw-rest-api-dynamodb-lambda-cdk/README.md | 31 ++++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/apigw-rest-api-dynamodb-lambda-cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/README.md index a4dd0ee6d..7870afe65 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/README.md +++ b/apigw-rest-api-dynamodb-lambda-cdk/README.md @@ -57,7 +57,7 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes 1. Invoke the DynamoDB **PutItem** action to add a new item to the DynamoDB table: * Enter the API URL with the **prod** stage as the path:. ``` - https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/id + https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/awsomedynamodb ``` * Select **POST** as the HTTP method from the drop-down list to the left of the address bar. * Choose the **Body** tab. Choose **raw** and select **JSON** from the drop-down list. Enter the following into the text box: @@ -73,11 +73,11 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes * Open the DynamoDB console and select the table which was created to confirm that the item has been added. * Change the values for `pk` or `data` in the POST body and repeat this process to add multiple items to the DynamoDB table. -1. Invoke the DynamoDB **Query** action to query items by artist in the DynamoDB table: +1. Invoke the DynamoDB **Query** action to query all items by artist in the DynamoDB table: * Enter the Invoke URL in the address bar. Add **/prod/foo** to the URL path. * Add **/foo** to the URL path. This defines the ID that you want to query. ``` - https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/${pk} + https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/awsomedynamodb ``` * Select **GET** as the HTTP method from the drop-down list to the left of the address bar. * Choose the **Body** tab. Choose **none**. @@ -98,6 +98,31 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes "ScannedCount": 1 } ``` +1. Invoke the DynamoDB **Query** action to query specific item by artist in the DynamoDB table: + * Enter the Invoke URL in the address bar. Add **/prod/foo** to the URL path. + * Add **/foo** to the URL path. This defines the ID that you want to query. + ``` + https://${API_ID}.execute-api.${REGION_NAME}.amazonaws.com/prod/awsomedynamodb/{id} + ``` + * Select **GET** as the HTTP method from the drop-down list to the left of the address bar. + * Choose the **Body** tab. Choose **none**. + * Choose **Send** to submit the request and receive a "200 OK" response with a list of the matching results. Example: + ``` + { + "Count": x, + "Items": [ + { + "pk": { + "S": "foo" + }, + "data": { + "S": "blah blah blah" + } + } + ], + "ScannedCount": 1 + } + ``` ## Documentation - [Tutorial: Build an API Gateway REST API with AWS integration](https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-aws-proxy.html) - [How do I use API Gateway as a proxy for another AWS service?](https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-proxy-integrate-service/) From eff1e75077d696a3ca2f4176062d6bf21eec7fa5 Mon Sep 17 00:00:00 2001 From: Thi Nguyen Date: Fri, 4 Aug 2023 17:18:34 +0700 Subject: [PATCH 8/9] Update readme --- apigw-rest-api-dynamodb-lambda-cdk/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apigw-rest-api-dynamodb-lambda-cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/README.md index 7870afe65..86164c7d1 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/README.md +++ b/apigw-rest-api-dynamodb-lambda-cdk/README.md @@ -98,6 +98,7 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes "ScannedCount": 1 } ``` + 1. Invoke the DynamoDB **Query** action to query specific item by artist in the DynamoDB table: * Enter the Invoke URL in the address bar. Add **/prod/foo** to the URL path. * Add **/foo** to the URL path. This defines the ID that you want to query. From d9bcafe5f86a2a298cd691dfd64846ce4d00790c Mon Sep 17 00:00:00 2001 From: Thi Nguyen Date: Mon, 7 Aug 2023 13:47:41 +0700 Subject: [PATCH 9/9] Update from Code review --- apigw-rest-api-dynamodb-lambda-cdk/README.md | 23 +++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/apigw-rest-api-dynamodb-lambda-cdk/README.md b/apigw-rest-api-dynamodb-lambda-cdk/README.md index 86164c7d1..485206b4e 100644 --- a/apigw-rest-api-dynamodb-lambda-cdk/README.md +++ b/apigw-rest-api-dynamodb-lambda-cdk/README.md @@ -24,7 +24,7 @@ The following diagram illustrates the solutions architect ``` 1. Change directory to the pattern directory: ``` - cd apigw-rest-api-dynamodb-lambda-cdk/cdk + cd apigw-rest-api-dynamodb-lambda-cdk ``` 1. Install dependencies @@ -37,6 +37,7 @@ The following diagram illustrates the solutions architect 1. Deploy the stack to your default AWS account and region. The output of this command should give you the HTTP API URL. ``` + cd .. cdk deploy ``` @@ -87,11 +88,17 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes "Count": 1, "Items": [ { - "pk": { + "AWSomeDynamoDBId": { "S": "foo" }, - "data": { + "name": { "S": "blah blah blah" + }, + "description": { + "S": "blah blah blah" + }, + "Id": { + "S": "string" } } ], @@ -113,11 +120,17 @@ Once the application is deployed, use [Postman](https://www.postman.com/) to tes "Count": x, "Items": [ { - "pk": { + "AWSomeDynamoDBId": { "S": "foo" }, - "data": { + "name": { "S": "blah blah blah" + }, + "description": { + "S": "blah blah blah" + }, + "Id": { + "S": "string" } } ],