Skip to content
Open
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
5 changes: 5 additions & 0 deletions javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
3 changes: 3 additions & 0 deletions javascript/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CDK asset staging directory
.cdk.staging
cdk.out
67 changes: 67 additions & 0 deletions javascript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
## Amazon CodeWhisperer Example

This repository accompanies a hands-on workshop that demonstrates how to leverage Amazon CodeWhisperer for building a fully fledged serverless app on AWS.

## Amazon CodeWhisperer

Amazon CodeWhisperer is a machine learning (ML)–powered service that helps improve developer productivity by generating code recommendations based on their comments in natural language and code in the integrated development environment (IDE).

With Amazon CodeWhisperer, developers can simply write a comment that outlines a specific task in plain English, such as “upload a file to S3.” Based on this, CodeWhisperer automatically determines which cloud services and public libraries are best suited for the specified task, builds the specific code on the fly, and recommends the generated code snippets directly in the IDE. Moreover, CodeWhisperer seamlessly integrates with your Visual Studio Code and JetBrains IDEs, in such a way that you can stay focused and never leave the development environment. See the [Amazon CodeWhisperer](https://aws.amazon.com/codewhisperer/) page for details.

## Try out Amazon CodeWhisperer

You can use this code repository to try out Amazon CodeWhisperer by building a full-fledged, event-driven, serverless application. With the aid of Amazon CodeWhisperer, you'll write your own code that runs on top of AWS Lambda to interact with Amazon DynamoDB, Amazon SNS, Amazon SQS, Amazon S3, and third-party HTTP APIs to perform image recognition using Amazon Rekognition. The users can interact with the application by sending the URL of an image for processing, or by listing the images and the objects present on each image.

### Architecture

![architecture](images/architecture.png)

### Prerequisites

To use the CodeWhisperer with this repo, you will need an AWS account and an active Amazon CodeWhisperer activation.

### Setup

Use the workshop description and follow the steps for building and deploying the application.

## Getting Help

Use the community resources below for getting help with AWS CodeGuru Reviewer.

- Use GitHub issues to report bugs and request features.
- Open a support ticket with [AWS Support](https://docs.aws.amazon.com/awssupport/latest/user/getting-started.html).
- For contributing guidelines, refer to [CONTRIBUTING](CONTRIBUTING.md).

## Contributing

See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.

## License

This project is licensed under the MIT-0 License. See the [LICENSE](LICENSE) file.

## Working with this CDK JavaScript project

The `cdk.json` file tells the CDK Toolkit how to execute your app. This project is set up like a standard NodeJS project.

You can install the required dependencies:

```bash
npm install
```

At this point you can now synthesize the CloudFormation template for this code.

```bash
cdk synth
```

## Useful commands

- `cdk ls` list all stacks in the app
- `cdk synth` emits the synthesized CloudFormation template
- `cdk deploy` deploy this stack to your default AWS account/region
- `cdk diff` compare deployed stack with current state
- `cdk docs` open CDK documentation

Enjoy!
81 changes: 81 additions & 0 deletions javascript/api/infrastructure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const { Stack, Duration } = require('aws-cdk-lib');
const s3 = require('aws-cdk-lib/aws-s3');
const lambda = require('aws-cdk-lib/aws-lambda');
const apigateway = require('aws-cdk-lib/aws-apigateway');
const sqs = require('aws-cdk-lib/aws-sqs');
const sns = require('aws-cdk-lib/aws-sns');
const sns_subs = require('aws-cdk-lib/aws-sns-subscriptions');
const s3n = require('aws-cdk-lib/aws-s3-notifications');

class APIStack extends Stack {

get sqsUrl() { return this.uploadQueueUrl }

get sqsArn() { return this.uploadQueueArn }

constructor(scope, id, props) {
super(scope, id, props);

const bucket = new s3.Bucket(this, "CW-Workshop-Images")

const imageGetAndSaveLambda = new lambda.Function(
this,
"ImageGetAndSaveLambda", {
functionName: "ImageGetAndSaveLambda",
runtime: lambda.Runtime.NODEJS_16_X,
code: lambda.Code.fromAsset("api/runtime"),
handler: "get-save-image.handler",
environment: { "BUCKET_NAME": bucket.bucketName }
}
)

bucket.grantReadWrite(imageGetAndSaveLambda)

const api = new apigateway.RestApi(
this,
"REST_API", {
restApiName: "Image Upload Service",
description: "CW workshop - upload image for workshop."
}
)

const getImageIntegration = new apigateway.LambdaIntegration(
imageGetAndSaveLambda, {
requestTemplates: { "application/json": '{ "statusCode": "200" }' }
}
)

api.root.addMethod("GET", getImageIntegration)

const uploadQueue = new sqs.Queue(
this,
"uploaded_image_queue", {
visibilityTimeout: Duration.seconds(30)
}
)

this.uploadQueueUrl = uploadQueue.queueUrl
this.uploadQueueArn = uploadQueue.queueArn

const sqsSubscription = new sns_subs.SqsSubscription(
uploadQueue, {
rawMessageDelivery: true
}
)

const uploadEventTopic = new sns.Topic(
this,
"uploaded_image_topic"
)

uploadEventTopic.addSubscription(sqsSubscription)

bucket.addEventNotification(
s3.EventType.OBJECT_CREATED_PUT,
new s3n.SnsDestination(uploadEventTopic)
)

}
}

module.exports = { APIStack }
51 changes: 51 additions & 0 deletions javascript/api/runtime/get-save-image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const fs = require('fs');
const https = require('https');
const AWS = require('aws-sdk')
const s3 = new AWS.S3()

// 1.) Function to download a file from URL
const downloadFileFromUrl = async (url, fileName) => {
return new Promise((resolve, reject) => {
fileName = `/tmp/${fileName}`;
const file = fs.createWriteStream(fileName);
https.get(url, response => {
response.pipe(file);
file.on('finish', () => {
file.close(() => resolve());
});
}).on('error', error => {
fs.unlink(fileName);
reject(error.message);
});
});
}

// 2.) Function to upload image to S3
const uploadImage = async (bucket, fileName) => {
const inputStream = fs.createReadStream(`/tmp/${fileName}`);

const params = {
Bucket: bucket,
Key: fileName,
Body: inputStream
}

const s3Response = await s3.upload(params).promise()
return s3Response
}

exports.handler = async function (event, context) {
const S3_BUCKET = process.env.BUCKET_NAME

const url = event.queryStringParameters.url
const name = event.queryStringParameters.name

// pass the output of method #1 as input to method #2
await downloadFileFromUrl(url, name)
await uploadImage(S3_BUCKET, name)

return {
statusCode: 200,
body: "Successfully Uploaded Img!"
}
}
28 changes: 28 additions & 0 deletions javascript/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env node

const cdk = require('aws-cdk-lib');
const { APIStack } = require('./api/infrastructure');
const { IntegrationStack } = require('./integration/infrastructure');
const { RekognitionStack } = require('./recognition/infrastructure');

const DEFAULT_REGION = 'us-east-2'

const defaultEnvironment = {
region: DEFAULT_REGION
}

const app = new cdk.App();

const apiStack = new APIStack(app, 'APIStack', { env: defaultEnvironment });
const integrationStack = new IntegrationStack(app, "IntegrationStack", { env: defaultEnvironment })
new RekognitionStack(
app,
"RekognitionStack", {
sqsUrl: apiStack.sqsUrl,
sqsArn: apiStack.sqsArn,
snsArn: integrationStack.snsArn,
env: defaultEnvironment
}
)

app.synth()
35 changes: 35 additions & 0 deletions javascript/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"app": "node app.js",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"package*.json",
"yarn.lock",
"node_modules"
]
},
"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"
]
}
}
50 changes: 50 additions & 0 deletions javascript/integration/infrastructure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { Stack, Duration } = require('aws-cdk-lib');
const lambda = require('aws-cdk-lib/aws-lambda');
const lambda_events = require('aws-cdk-lib/aws-lambda-event-sources');
const sqs = require('aws-cdk-lib/aws-sqs');
const sns = require('aws-cdk-lib/aws-sns');
const sns_subs = require('aws-cdk-lib/aws-sns-subscriptions');

class IntegrationStack extends Stack {

get snsArn() { return this.rekognizedEventTopicArn }

constructor(scope, id, props) {
super(scope, id, props);

const rekognizedQueue = new sqs.Queue(
this,
"rekognized_image_queue", {
visibilityTimeout: Duration.seconds(30)
}
)

const sqsSubscription = new sns_subs.SqsSubscription(
rekognizedQueue, {
rawMessageDelivery: true
}
)

const rekognizedEventTopic = new sns.Topic(
this,
"rekognized_image_topic"
)

this.rekognizedEventTopicArn = rekognizedEventTopic.topicArn
rekognizedEventTopic.addSubscription(sqsSubscription)

const integrationLambda = new lambda.Function(
this,
"IntegrationLambda", {
runtime: lambda.Runtime.NODEJS_16_X,
handler: "send-email.handler",
code: lambda.Code.fromAsset("integration/runtime")
}
)

const invokeEventSource = new lambda_events.SqsEventSource(rekognizedQueue)
integrationLambda.addEventSource(invokeEventSource)
}
}

module.exports = { IntegrationStack }
49 changes: 49 additions & 0 deletions javascript/integration/runtime/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions javascript/integration/runtime/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "send-email",
"version": "0.1.0",
"bin": {
"send-mail": "send-email.js"
},
"scripts": {
"build": "echo \"The build step is not required when using JavaScript!\" && exit 0"
},
"dependencies": {
"xml-js":"1.6.11"
}
}
Loading